RedX
RedX

Reputation: 15165

Templated class constructor uses wrong overload when in struct

The test is as following:

class NotInit{};

NotInit NOT_INIT;

template<class T>
class Optional{

  T value;
  bool has_value;

public:
  Optional() : value(), has_value(false){}
  explicit Optional(NotInit):value(), has_value(false) {}
  explicit Optional(T const & val):value(val), has_value(true){}
  explicit Optional(Optional<T> const & other) {}

  Optional& operator=(Optional<T> const & other){}
  Optional& operator=(T const & other){}
};

enum X {
  FIRST
};

struct Some {
  Optional<X> member;
};

int main(int, char**){
  Optional<X> const opt(NOT_INIT);
  Some s = {NOT_INIT};
  return 0;
}

Clang 3.4 complains:

../st.cpp:31:12: error: no viable conversion from 'NotInit' to 'Optional<X>'
        Some s = {NOT_INIT};
                  ^~~~~~~~
1 error generated.

Why does it complain for the struct initialization but ont for the varaiable? Why is it not choosing the constructor with the proper parameter?

What overload is missing so that i can use this to init the struct?

I cannot use boost and i'm not sure this error would not appear if using boost::optional.

Upvotes: 0

Views: 109

Answers (1)

Rerito
Rerito

Reputation: 6086

You are marking your constructors explicit. Thus when you are trying to initialize your struct Some with a brace-initializer list, you are triggering an implicit conversion...

This is prevented here:

class NotInit;
template <typename T>
class Optional {
    // Here => Optional(NotInit) cannot be called implicitly
    explicit Optional(NotInit):value(), has_value(false) {}
};
/* ... */
Some s = {NOT_INIT}; // Implicit call to Optional<X>(NotInit): whoops!

If you remove the explicit, you can keep:

Some s = {NOT_INIT};

If you chose not to, then you'll have to instantiate s like this:

Some s = {Optional<X>(NOT_INIT)};

In any case, you'll have to remove the explicit keyword on your copy constructor (which has to perform a copy and not be a "dummy" constructor).
Why? Because once the Optional<X> object is built from NOT_INIT, a copy has to be made to s.member.

Note: You could also provide a move constructor, which would be more appropriate in that case, since the object you get from the conversion of NOT_INITis an rvalue.

Upvotes: 1

Related Questions