I have a class with copy & move ctor deleted.
struct A
{
A(int a):data(a){}
~A(){ std::cout << \"~A()\" << this << \" : \" <<
This is bad:
auto q = std::tuple(A{100},A{200});
you are constructing a tuple
of rvalue references to temporaries that get destroyed at the end of the expression, so you're left with dangling references.
The correct statement would be:
std::tuple q(100, 200);
However, until quite recently, the above was not supported by the standard. In N4296, the wording around the relevant constructor for tuple
is [tuple.cnstr]:
template
constexpr explicit tuple(UTypes&&... u); Requires:
sizeof...(Types) == sizeof...(UTypes)
.is_constructible
is true for all::value i
.
Effects: Initializes the elements in the tuple with the corresponding value instd::forward
.(u)
Remark: This constructor shall not participate in overload resolution unless each type inUTypes
is implicitly convertible to its corresponding type inTypes
.
So, this constructor was not participating in overload resolution because int
is not implicitly convertible to A
. This has been resolved by the adoption of Improving pair and tuple, which addressed precisely your use-case:
struct D { D(int); D(const D&) = delete; };
std::tuple td(12); // Error
The new wording for this constructor is, from N4527:
Remarks: This constructor shall not participate in overload resolution unless
sizeof...(Types) >= 1
andis_constructible
is true for all::value i
. The constructor is explicit if and only ifis_convertible
is::value false
for at least one i.
And is_constructible::value
is true.
To present the difference another way, here is an extremely stripped down tuple implementation:
struct D { D(int ) {} D(const D& ) = delete; };
template
struct Tuple {
Tuple(const T& t)
: T(t)
{ }
template ::value>
#else
typename = std::enable_if_t::value>
#endif
>
Tuple(U&& u)
: t(std::forward(u))
{ }
T t;
};
int main()
{
Tuple t(12);
}
If USE_OLD_RULES
is defined, the first constructor is the only viable constructor and hence the code will not compile since D
is noncopyable. Otherwise, the second constructor is the best viable candidate and that one is well-formed.
The adoption was recent enough that neither gcc 5.2 nor clang 3.6 actually will compile this example yet. So you will either need a newer compiler than that (gcc 6.0 works) or come up with a different design.