エラー付き戻り値クラステンプレート

もうだいぶ前だが、http://www.kmonos.net/wlog/88.html とか http://d.hatena.ne.jp/gnarl/20071216/1197774166 とか見て、エラーの処理を呼び出し側で柔軟にできるようにならないかと思って書いた(んだと思う)。
error.hpp

#if !defined ERROR_HPP_INCLDUED_
#define ERROR_HPP_INCLUDED_

#include <new>
#include <boost/aligned_storage.hpp>
#include <boost/type_traits/alignment_of.hpp>
#include <boost/shared_ptr.hpp>


namespace exist {

namespace error_detail {

struct error_holder_base {
virtual void throw_() const = 0;
virtual ~error_holder_base() {}
error_holder_base() {}
private:
error_holder_base(error_holder_base const &);
void operator=(error_holder_base const &);
};


template<typename E>
class error_holder : public error_holder_base {
E const err_core_;
public:
error_holder(E const & err_core) : err_core_(err_core) {}
~error_holder() {}
void throw_() const {
throw err_core_;
}
};


class error_base {
protected:
boost::shared_ptr<error_holder_base const> const err_;
~error_base() {}

public:
error_base(error_base const & org) : err_(org.err_) {}
error_base(error_holder_base const * err) : err_(err) {}

void throw_error() const {
if (err_) {
err_->throw_();
}
}
};

} // namespace error_detail {


template<typename T>
struct error : public error_detail::error_base {
typedef T value_type;

private:
typedef T & (error::*bool_type)() const;
typename boost::aligned_storage<
sizeof(T),
boost::alignment_of<T>::value> mutable
storage_;


bool operator==(error const &) const;
bool operator!=(error const &) const;

public:
error(T val) : error_base(0) {
new(storage_.address()) T(val);
}


template<typename E>
error(E const & err_obj) : error_base(new error_detail::error_holder<E>(err_obj)) {}
error(error const & obj) : error_base(obj) {
if (obj) {
new(storage_.address()) T(*obj);
}
}
template<typename U>
error(error<U> const & obj) : error_base(obj) {
if (obj) {
new(storage_.address()) T(*obj);
}
}


~error() {
if (*this) {
static_cast<T *>(storage_.address())->~T();
}
}


T * get() const {
if (err_) {
throw_error();
}
return static_cast<T *>(storage_.address());
}


T & operator*() const {
return *get();
}


T * operator->() const {
return get();
}


operator bool_type() const {
return err_ ? 0 : static_cast<bool_type>(&error::operator*);
}

};

} // namespace exist {

#endif // ERROR_HPP_INCLUDED_

サンプル

#include <iostream>
#include <stdexcept>
#include <exist/error.hpp>

using std::cout;
using std::endl;

exist::error<int> success() {
return 42;
}

exist::error<int> failed() {
return std::runtime_error("error");
}

int main() {
exist::error<int> ret1 = success();
if (ret1) {
cout << "success : " << *ret1 << endl;
}
exist::error<int> ret2 = failed();
if (!ret2) {
cout << "failed" << endl;
}
try {
cout << *ret2 << endl;
} catch (std::runtime_error const & e) {
cout << "catch :" << e.what() << endl;
}
ret2.throw_error();
return 0;
}

Boost.Optionalにエラーの時は任意のオブジェクトを持たせられるようした感じ。エラーオブジェクトを保持している場合、derefecenceしようとすると、例外としてそれが投げられる。あとエラーオブジェクトを保持していて、かつ未チェックの場合にも例外を投げてやりたいのだが、それをしようとするとどうしてもデストラクタの中で例外を投げることになってしまう。これは非常によろしくない。なんとかならんものか。
追記:error_holder_baseの純粋仮想デストラクタの実装がクラス外にあったのだが、それだと複数の翻訳単位で使用されると多重定義でエラーになる。この件は本題ではないので、とりあえず純粋じゃなくした上でその実装をクラス内に移した。