我正在尝试使用(隐式)转换运算符创建一个名为tuple_cnv
的类来构造元组中的任何对象(如C ++ 17 std::make_from_tuple
函数),但是是递归的自然,以这种方式,如果一个元组由其他元组组成,它会将任何“nner元组”转换为tuple_cnv
,以允许目标类型的递归就地构造:
#include <iostream>
#include <utility>
#include <tuple>
#include <functional>
struct A { int i1, i2, i3; };
struct B { A a1, a2; };
template<class T> struct tuple_cnv;
template<class... Ts>
struct tuple_cnv<std::tuple<Ts...> >
{
using tuple_t = std::tuple<Ts...>;
std::reference_wrapper<tuple_t const> ref;
tuple_cnv(tuple_t const& t) : ref(t) {}
template<class T>
operator T() const
{ return p_convert<T>(std::index_sequence_for<Ts...>{}); }
private:
template<class T>
static T const& p_convert(T const& t) { return t; }
template<class... Tss>
static tuple_cnv<Tss...> p_convert(std::tuple<Tss...> const& t)
{ return tuple_cnv<std::tuple<Tss...> >(t); }
template<class T, std::size_t... I>
T p_convert(std::index_sequence<I...>) const
{ return {p_convert(std::get<I>(ref.get()))...}; }
};
template<class T>
auto make_tuple_cnv(T const& t) { return tuple_cnv<T>(t); }
using tup = std::tuple<std::tuple<int, int, int>, std::tuple<int, int, int> >;
int main()
{
tup t{{3, 4, 5}, {1, 7, 9}};
// Equivalent to: B b{{3,4,5}, {1,7,9}};
B b = make_tuple_cnv(t);
std::cout << b.a2.i3 << std::endl;
}
如有疑问,行:
{p_convert(std::get<I>(ref.get()))...}
必须在逗号分隔的元素列表中扩展元组(在{...}
内部以获取初始化列表),但用相应的tuple_cnv
替换每个元组元素以允许创建初始化树的一个树 - 在构造对象tuple_cnv
时通过每个内部T
的(隐式)转换运算符列出。
请参阅main
函数中注释的“预期等效”表达式。
问题是我的编译器错误太大,以至于我无法理解我的实现有什么问题:
main.cpp: In instantiation of 'T tuple_cnv<std::tuple<_Tps ...> >::p_convert(std::index_sequence<I ...>) const [with T = B; long unsigned int ...I = {0, 1}; Ts = {std::tuple<int, int, int>, std::tuple<int, int, int>}; std::index_sequence<I ...> = std::integer_sequence<long unsigned int, 0, 1>]':
main.cpp:28:26: required from 'tuple_cnv<std::tuple<_Tps ...> >::operator T() const [with T = B; Ts = {std::tuple<int, int, int>, std::tuple<int, int, int>}]'
main.cpp:53:27: required from here
main.cpp:40:51: error: could not convert '{tuple_cnv<std::tuple<std::tuple<int, int, int>, std::tuple<int, int, int> > >::p_convert<std::tuple<int, int, int> >((* & std::get<0, std::tuple<int, int, int>, std::tuple<int, int, int> >((* &((const tuple_cnv<std::tuple<std::tuple<int, int, int>, std::tuple<int, int, int> > >*)this)->tuple_cnv<std::tuple<std::tuple<int, int, int>, std::tuple<int, int, int> > >::ref.std::reference_wrapper<const std::tuple<std::tuple<int, int, int>, std::tuple<int, int, int> > >::get())))), tuple_cnv<std::tuple<std::tuple<int, int, int>, std::tuple<int, int, int> > >::p_convert<std::tuple<int, int, int> >((* & std::get<1, std::tuple<int, int, int>, std::tuple<int, int, int> >((* &((const tuple_cnv<std::tuple<std::tuple<int, int, int>, std::tuple<int, int, int> > >*)this)->tuple_cnv<std::tuple<std::tuple<int, int, int>, std::tuple<int, int, int> > >::ref.std::reference_wrapper<const std::tuple<std::tuple<int, int, int>, std::tuple<int, int, int> > >::get()))))}' from '<brace-enclosed initializer list>' to 'B'
{ return {p_convert(std::get<I>(ref.get()))...}; }
^
编译器错误是什么?什么是我看不到的?
注意:根据@Barry的建议,我使用apply
更改了实现,但调用了tuple_to_args
,因为实现不完全等效({{1使用std::apply
,处理不同类型的函数,如指向成员函数的指针):
std::invoke
使用template<class... Ts>
constexpr auto indexes(std::tuple<Ts...> const&)
{ return std::index_sequence_for<Ts...>{}; }
template<class fun_t, class tuple_t, std::size_t... I>
decltype(auto) tuple_to_args(fun_t&& f, tuple_t&& tuple, std::index_sequence<I...> const&)
{ return f(std::get<I>(std::forward<tuple_t>(tuple))...); }
template<class fun_t, class tuple_t>
decltype(auto) tuple_to_args(fun_t&& f, tuple_t&& t)
{ return tuple_to_args(std::forward<fun_t>(f), std::forward<tuple_t>(t), indexes(t)); }
作为辅助函数,转换运算符的实现已更改为:
tuple_to_args
还删除了非静态template<class T>
operator T() const
{
auto inner_f = [](auto&&... tuple) -> T {
return {p_convert(std::forward<decltype(tuple)>(tuple))...};
};
return tuple_to_args(inner_f, ref.get());
}
函数,但编译器错误仍然非常相似:
p_convert
答案 0 :(得分:4)
问题在于:
template<class... Tss>
static tuple_cnv<Tss...> p_convert(std::tuple<Tss...> const& t)
{ return tuple_cnv<std::tuple<Tss...> >(t); }
你尽可能地努力找到错误。你有两个同名的函数做不同的事情(p_convert()
给你一个T
和p_convert()
那个处理递归)。那令人困惑。
相反,实现apply
(因为你在C ++ 14上)。然后使用apply
:
template <class T>
operator T() const {
return std::apply([](auto const&... elems) -> T {
return {p_convert(elems)...};
}, ref);
}
答案 1 :(得分:0)
问题是p_convert
函数返回的值无效。它没有返回tuple_cnv<std::tuple<Ts...> >
,而是返回了tuple_cnv<Ts...>
。
由于tuple_cnv<Ts...>
不是无效类型,因为没有tuple
实例化允许非元组类型,编译器已经替换了#34;未知类型&#34;,到int
,因为在旧C中,当变量没有指定类型时(在非常旧的C中,可以在没有指定显式类型的情况下引入变量),默认为int
。
因此,编译器试图将内部std::tuple<int, int, int>
转换为int
,这不是无效的转换。
编写return B{p_convert(std::forward<decltype(tuple)>(tuple))...}
时显示了不错的编译器错误输出,而不仅仅是初始化列表,它显示的是完整表达式而不是已解析的类型。
这是完整的代码,但有一些改进:
#include <iostream>
#include <utility>
#include <tuple>
#include <functional>
struct A { int i1, i2, i3; };
struct B { A a1, a2; };
template<class... Ts>
constexpr auto indexes(std::tuple<Ts...> const&)
{ return std::index_sequence_for<Ts...>{}; }
namespace impl {
template<class fun_t, class tuple_t, std::size_t... I>
decltype(auto) tuple_to_args(fun_t&& f, tuple_t&& tuple, std::index_sequence<I...> const&)
{ return f(std::get<I>(std::forward<tuple_t>(tuple))...); }
}
template<class fun_t, class tuple_t>
decltype(auto) tuple_to_args(fun_t&& f, tuple_t&& t)
{ return impl::tuple_to_args(std::forward<fun_t>(f), std::forward<tuple_t>(t), indexes(t)); }
namespace impl {
template<class T>
struct tuple_cnv;
}
template<class T>
auto tuple_cnv(T const& t) { return impl::tuple_cnv<T>(t); }
namespace impl {
template<class tuple_t>
class tuple_cnv
{
std::reference_wrapper<tuple_t const> ref;
public:
explicit tuple_cnv(tuple_t const& t) : ref(t) {}
template<class T>
operator T() const
{
auto inner_f = [](auto&&... elements) -> T {
return {p_convert(std::forward<decltype(elements)>(elements))...};
};
return ::tuple_to_args(inner_f, ref.get());
}
private:
template<class T>
static decltype(auto) p_convert(T&& t) { return std::forward<T>(t); }
template<class... Tss>
static auto p_convert(std::tuple<Tss...> const& t)
{ return ::tuple_cnv(t); }
};
}
using tup = std::tuple<std::tuple<int, int, int>, std::tuple<int, int, int> >;
int main()
{
tup t{{3, 4, 5}, {1, 7, 9}};
B b = tuple_cnv(t);
std::cout << b.a2.i3 << '\n'; // It prints 9
}