Why can't std::tuple be element-wise construct

2019-04-20 03:09发布

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 is true for all i.

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?

1条回答
放荡不羁爱自由
2楼-- · 2019-04-20 03:48

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.

查看更多
登录 后发表回答