问题
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
?
回答1:
In C++ 14, there doesn't seem to be any way of initializing tuple elements when constructors are explicit, because of the is_convertible
requirement. I ended up writing a bare-bones implementation of std::tuple
myself that is used on C++14 implementations, such as on Debian 8.
回答2:
I have different error with gcc 4.9.2 than you:
main_console.cpp:47:30: error: no matching function for call to 'std::tuple<A, A, A, A>::tuple(int&, int&, int&, int&)' ts((sizeof(Ts), i)...)
Problem is in the constructor B(int i) : ts((sizeof(Ts), i)...) { }
and to be honest i do not know how is your constructor working.
When the B
constructor looks like: B(int i) : ts(Ts(i)...) { }
and with the copy constructor in A
it works fine for me.
来源:https://stackoverflow.com/questions/36580855/construct-tuple-by-passing-the-same-argument-to-each-element-with-explicit-const