Pergunta

Currently, one of my toy class templates has two constructors that look very similar:

optional(const T& x)
{
    construct(x);
}

optional(T&& x)
{
    construct(std::move(x));
}

Can I combine them into a single constructor template, or will this change the semantics somehow?

template<typename U>
optional(U&& x)
{
    construct(std::forward<U>(x));
}
Foi útil?

Solução

It changes the way traits such as std::is_constructible and std::is_convertible interact with optional. For example given:

class A {};

int main()
{
    std::cout << std::is_constructible<optional<A>, int>::value << '\n';
};

Your original code would print out:

0

But your new code will print out:

1

If this is undesirable, and you still want to go with your new code, you could enable_if it to restrict U to acceptable types.

The only other possible issue I see is if T can be a reference type (e.g. int&). In that case the second constructor of your original code looks suspicious as it would be passing in an rvalue, and you might be trying to bind that rvalue to a non-const lvalue reference (can't tell for sure). If T can never be a reference type, no need to worry about this.

Outras dicas

A templated constructor will never be (considered by the compiler to be) a copy constructor, sorry.

Oh, construct is not a constructor, it's member function template.

Then possibly the semantics are different. Your original overload set passes either an lvalue reference to const or an rvalue reference to non-const. The template version can pass an lvalue reference to non-const as well. (I'm ignoring rvalue references to const.)

In all likeliness though construct is either declared to accept U&& (else your previous overload that moves a T wouldn't work) and in this case should deal with non-const lvalues, or is declared to accept U const& and in this case it's harmless.

Assuming you have at least construct(const T& x) and possibly the addition of construct(T&& x) defined, and your object of type U is convertable to an object of type T, I think you should be fine ...

  1. A constant l-value reference of type U will bind to construct(const T& x)
  2. A non-constant l-value reference of type U will bind to construct(const T& x)
  3. A temporary r-value of type U will bind to construct(T&& x) or construct(const T& x) if the r-value reference version is not defined
  4. A constant r-value reference of type U will bind to construct(const T& x)
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top