The following works fine on Visual C++ 2015 Update 2. Note that A
is non-copyable and A::A
is explicit
.
#include <iostream>
#include <tuple>
struct A
{
explicit A(int i)
{
std::cout << i << " ";
}
// non-copyable
A(const A&) = delete;
A& operator=(const A&) = delete;
};
template <class... Ts>
struct B
{
std::tuple<Ts...> ts;
B(int i)
: ts((sizeof(Ts), i)...)
{
}
};
int main()
{
B<A, A, A, A> b(42);
}
The goal is to pass the same argument to all tuple elements. It correctly outputs:
42 42 42 42
However, it fails to compile on g++ 4.9.2. Among the many messages is the tuple
constructor overload that I think should be called:
In instantiation of ‘B<Ts>::B(int) [with Ts = {A, A, A, A}]’:
33:24: required from here
25:30: error: no matching function for call to
‘std::tuple<A, A, A, A>::tuple(int&, int&, int&, int&)’
: ts((sizeof(Ts), i)...)
[...]
/usr/include/c++/4.9/tuple:406:19: note: template<class ... _UElements, class>
constexpr std::tuple< <template-parameter-1-1> >::tuple(_UElements&& ...)
constexpr tuple(_UElements&&... __elements)
^
/usr/include/c++/4.9/tuple:406:19: note: template argument deduction/substitution failed:
/usr/include/c++/4.9/tuple:402:40: error: no type named ‘type’ in
‘struct std::enable_if<false, void>’
template<typename... _UElements, typename = typename
The function signature is incomplete in the message, but it refers to this one:
template<typename... _UElements, typename = typename
enable_if<__and_<is_convertible<_UElements,
_Elements>...>::value>::type>
explicit constexpr tuple(_UElements&&... _elements)
: _Inherited(std::forward<_UElements>(__elements)...) { }
My understanding is that is_convertible
fails for an explicit constructor. g++ 5.1 and clang 3.5 have similar error messages.
Now, in C++14, 20.4.2.1/10 says: "This constructor shall not participate in overload resolution unless each type in UTypes
is implicitly convertible to its corresponding type in Types
". This gives me the impression that g++ and clang actually have this right and that Visual C++ is overly permissive.
[edit: It appears that C++17 has removed this restriction and that Visual C++ 2015 follows it. It now says: "This constructor shall not participate in overload resolution unless [...] is_constructible<Ti, Ui&&>::value
is true
for all i
." It looks like "is implicitly convertible" was changed to "is_constructible
". However, I still need a C++14 solution.]
I tried removing explicit
from the constructor (I'd prefer to keep it). Visual C++ again compiles fine, but both g++ and clang complain about the deleted copy constructor. Because int
is now implicitly convertible to an A
, I seem to end up in
explicit constexpr tuple(const Types&...)
which would implicitly convert the int
s into a bunch of A
s and then try to copy them. I'm actually not sure how I would ever be able to use the other constructor.
In C++14, how can I get tuple
to initialize its elements by passing the same argument to each constructor if the constructors are explicit
?