I can't initialize std::tuple
elements element-wise from a std::tuple
of compatible types. Why doesn't it work as with boost::tuple
?
#include <tuple>
#include <boost/tuple/tuple.hpp>
template <typename T>
struct Foo
{
// error: cannot convert 'std::tuple<int>' to 'int' in initialization
template <typename U>
Foo(U &&u) : val(std::forward<U>(u)) {}
T val;
};
int main()
{
boost::tuple<Foo<int>>{boost::tuple<int>{}}; // ok
auto a = boost::tuple<int>{};
boost::tuple<Foo<int>>{a}; // ok
std::tuple<Foo<int>>{std::tuple<int>{}}; // fails with rvalue
auto b = std::tuple<int>{};
std::tuple<Foo<int>>{b}; // fails with lvalue
}
Live on Coliru (GCC or Clang and libstdc++ does not compile, however Clang and libc++ compiles without errors)
std::tuple
is not doing element-wise construction and it instantiates Foo<int>::Foo<std::tuple<int>>
instead of Foo<int>::Foo<int>
. I thought std::tuple::tuple
overloads no. 4 and 5 were exactly for that purpose:
template <class... UTypes>
tuple(const tuple<UTypes...>& other);
template <class... UTypes>
tuple(tuple<UTypes...>&& other);
Note:
Does not participate in overload resolution unless
std::is_constructible<Ti, const Ui&>::value
istrue
for alli
.
std::is_constructible<Foo<int>, int>::value
is true
. From the GCC template error, I can see that overload no. 3:
template <class... UTypes>
explicit tuple(UTypes&&... args);
is selected instead. Why?
The new std::array and std::tuple containers provide developers with additional ways to manage structured data efficiently.
Class template std::tuple is a fixed-size collection of heterogeneous values. It is a generalization of std::pair. If std::is_trivially_destructible<Ti>::value is true for every Ti in Types , the destructor of tuple is trivial.
It is fully expected that std::tuple will be slower than std::pair when not optimized, because it is more complicated object. A pair has exactly two members, so its methods are straightforward to define. But tuple has arbitrary number of members and the only way to iterate over template argument list is with recursion.
The tuple itself isn't mutable (i.e. it doesn't have any methods that for changing its contents). Likewise, the string is immutable because strings don't have any mutating methods. The list object does have mutating methods, so it can be changed.
Overloads (4) and (5) are poorer matches than (3) when passed a tuple&
: they are const&
and &&
overloads, while (3) matches exactly through the magic of perfect forwarding.
(3) is valid because your Foo(U&&)
constructor is overly greedy.
Add SFINAE checks to Foo(U&&)
so that it fails to match when it fails to build:
template <class U,
std::enable_if_t<std::is_convertible<U,int>{},int>* =nullptr
>
Foo(U &&u) : val(std::forward<U>(u)) {}
The rvalue case should, however, work or be ambiguous. Looking at the error log of your live example, the only error I see is with the lvalue one.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With