Why can't std::tuple be element-wise constructed with a std::tuple of compatible types?

和自甴很熟 提交于 2019-12-21 07:28:24

问题


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:


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.



来源:https://stackoverflow.com/questions/38281724/why-cant-stdtuple-be-element-wise-constructed-with-a-stdtuple-of-compatible

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!