Is it really such a problem to have this object in every translation unit?
Is it beyond current linker technology to merge these identical copies,
thus preventing binary bloat?
If it is, you could use something like the following solution:
template<typename T>
class optional {
public:
optional(none_t) { /* whatever */ }
private:
enum dummy{};
optional(dummy *);
};
This causes users attempting to initialize optional with a literal 0 or
nullptr to receive an error about ambiguous overloading. However, it still
allows it to be used as:
optional<int> oi(0);
if you also have a public constructor that accepts a T const & (which you
do). The only tricky part is code like the following:
optional op0(0);
optional opn(nullptr);
Both of those give ambiguous overloads. The first, from integer literal 0,
already does not work, because the template parameter in your current
constructor is deduced as int, and there is no implicit conversion from int
to int *. C++03 users would have to say something like static_cast(0). The second, however, currently compiles, and we probably want to
preserve this behavior. Note that if we had said optional opn =
nullptr, it would fail. We probably want that to work just like explicit
construction.
This means we need to selectively enable and disable some of these
constructors under certain conditions. I believe the following will work
for your needs:
// Same none_t and none definition as in boost now, just so people can see
it without looking it up
namespace detail { struct none_helper{}; }
typedef int detail::none_helper::*none_t ;
none_t const none = (static_cast(0)) ;
template<typename T>
class optional {
public:
template<typename U>
optional(U, typename std::enable_if::value,
int>::type * = 0): m_b(false) {}
optional(T): m_b(true) {}
operator bool() const { return m_b; }
private:
bool m_b;
enum dummy{};
template<typename U>
optional(U, typename std::enable_if::value,
int>::type * = 0);
};
I used C++11 type traits because I can never remember when I need to use
the _c versions of boost type traits, but it should be fairly
straightforward to translate that for someone more familiar with the boost
versions.
The following code compiles and runs. Commented out lines do not compile.
class C {};
int main() {
boost::optional<int> bi(0);
assert(bi);
boost::optional<int> bia = 0;
assert(bia);
// boost::optional<C> bc(0);
boost::optional<C> bca = 0;
assert(!bca);
boost::optional bpn(nullptr);
assert(bpn);
// boost::optional bpna = nullptr;
boost::optional bp0c(static_cast(0));
assert(bp0c);
boost::optional bp0ca = static_cast(0);
assert(bp0ca);
// boost::optional bp0(0);
// boost::optional bpoa = 0;
optional<int> ni(0);
assert(ni);
optional<int> nia = 0;
assert(nia);
// optional<C> nc(0);
// optional<C> nca = 0;
optional<C> ncu(none);
assert(!ncu);
optional<C> ncua = none;
assert(!ncua);
optional npn(nullptr);
assert(npn);
optional npna = nullptr;
assert(npna);
optional np0c(static_cast(0));
assert(np0c);
optional np0ca = static_cast(0);
assert(np0ca);
optional np0(0);
assert(np0);
optional np0a = 0;
assert(np0a);
optional npu(none);
assert(!npu);
optional npua = none;
assert(!npua);
}
Are all of these test cases the correct result?