Я не могу инициализировать std::tuple
элементы поэлементно из std::tuple
совместимых типов. Почему это не работает как с 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
}
Жить на Колиру (GCC или Clang и libstdc ++ не компилируются, однако Clang и libc ++ компилируются без ошибок)
std::tuple
не делает поэлементное строительство, и это создает Foo<int>::Foo<std::tuple<int>>
вместо Foo<int>::Foo<int>
, я думал std::tuple::tuple
перегрузок нет. 4 и 5 были именно для этого:
template <class... UTypes>
tuple(const tuple<UTypes...>& other);
template <class... UTypes>
tuple(tuple<UTypes...>&& other);
Замечания:
Не участвует в разрешении перегрузки, если только
std::is_constructible<Ti, const Ui&>::value
являетсяtrue
для всехi
,
std::is_constructible<Foo<int>, int>::value
является true
, Из ошибки шаблона GCC я вижу, что перегрузки нет. 3:
template <class... UTypes>
explicit tuple(UTypes&&... args);
выбран вместо Зачем?
Перегрузки (4) и (5) являются худшими совпадениями, чем (3), когда переданы tuple&
: они есть const&
а также &&
перегрузки, в то время как (3) точно соответствует магии совершенной пересылки.
(3) действует, потому что ваш Foo(U&&)
конструктор слишком жадный.
Добавить чеки SFINAE в Foo(U&&)
так что он не может соответствовать, когда он не может построить:
template <class U,
std::enable_if_t<std::is_convertible<U,int>{},int>* =nullptr
>
Foo(U &&u) : val(std::forward<U>(u)) {}
Тем не менее, случай должен работать или быть неоднозначным. Глядя на журнал ошибок вашего живого примера, я вижу только ошибку lvalue.
Других решений пока нет …