可变模板容器类中可以使用无模板的getter吗?

Are Template-less Getters Possible in Variadic Template Container Classes?

本文关键字:getter 可以使 容器类      更新时间:2023-10-16

一个假想的可变模板元组类,据我所知,必须对模板参数使用getter。

int MyInt = MyTuple.Get<int>(0);

这是不方便的,并引入了潜在的错误。我不禁觉得,有一种方法可以构造这个类,这样你就不必显式地指定它了。

int MyInt = MyTuple.Get(0);

我的第一个想法是Get()成员函数返回另一个类,该类可以自己计算类型,可能是通过将typeid(Foo).name()与某些预先计算的列表中的值进行比较。但是,这仍然必须在运行时之前发生,并且我无法找到在编译时迭代类似内容的方法。

对于可变的模板容器类(如元组),是否有办法拥有不需要显式指定类型的getter ?

你说喜欢std::tuple吗?

getter的模板实参指定成员的索引,而不是类型。根据定义,元组的数量和类型在编译时是固定的。因为类型依赖于索引,并且类型必须在编译时已知,所以getter必须被模板化。

template< typename ... types >
struct tuple;
template< typename head, typename ... tail_types >
struct tuple {
    head value;
    tuple< tail_types ... > tail;
};
template<>
struct tuple<> {};
template< typename tuple, size_t index >
struct tuple_element;
template< typename head, typename ... tail, size_t index >
struct tuple_element< tuple< head, tail ... >, index >
    { typedef typename tuple_element< tail ..., index - 1 >::type type; };
template< typename head, typename ... tail >
struct tuple_element< tuple< head, tail ... >, 0 >
    { typedef head type; };
template< size_t index, typename tuple >
typename std::enable_if< index != 0, 
                   typename tuple_element< tuple, index >::type >::type
get( tuple const &t )
    { return get< index - 1 >( t.tail ); }
template< size_t index, typename tuple >
typename std::enable_if< index == 0,
                   typename tuple_element< tuple, index >::type >::type
get( tuple const &t )
    { return t.value; }

等。

c++的规则通常不允许你所要求的。

结构体是已知类型的值的集合,通过编译时必须提供的名称来访问这些值。最后一部分是至关重要的,因为它允许c++编译器知道你谈论的是哪个值。

A tuple是已知类型的值的集合,这些值可以通过数字访问。然而,仅仅因为它是一个数字并不意味着它可以是运行时定义的数字,就像为结构体的成员提供字符串名称并不意味着您可以在运行时获得该成员一样。

tuple的每个成员都是类型化的,c++需要在编译时知道它们的类型才能运行。类似地,任何函数的返回类型在编译时都是已知的;它不能根据参数的值更改。你可以根据类型选择不同的函数(函数重载或模板参数推导),但不能基于值,因为值(除非它们是编译时常量)只有在运行时才知道。

为了做你想做的事,你将不得不以某种方式打破c++类型系统。例如,理论上可以有一个返回无类型指针的get函数。或者,如果您至少需要一些类型安全的伪装,可以使用boost::anytuple类型的boost::variant。但这是你能做到的最好的了。

即使您在编译时指定了类型(get<Type>),由于类型不匹配的可能性,这仍然不够好。然后你做什么?

这个概念在像c++这样的编译时类型语言中是行不通的。

MyTuple.Get(0);这种Get声明的返回类型是什么?如果你同意它返回元组中所有可能类型的变体,那么你可以让它工作,是的。

另一方面,Boost。Tuple和c++ 0x Tuple将索引作为模板参数。没有潜在的错误。看看吧。

标准库包括std::get函数模板,它与std::tuplestd::pair一起工作,不需要显式指定类型。它通过将索引作为模板参数来工作。

std::tuple<int,std::string,char const*> t(1,"foo","blah");
auto i = std::get<0>(t); // i is int
auto s = std::get<1>(t); // s is std::string
auto p = std::get<2>(t); // p is char const*

返回类型在编译时必须始终是已知的,并且它取决于索引,因此您不可能将索引作为运行时参数。

这不是假设。Boost::tuple按照您描述的方式运行。

boost::tuple<int, float> myTuple = boost::make_tuple(10, 3.1);
int myInt = myTuple.Get<0>();

所以您已经得到了一些回复,解释如果您将索引作为编译时参数传递,这将有效。不过,我想对总体思路进行评论:

我想我已经看到了返回一个具有模板化隐式转换操作符的代理的技巧:

template<typename T> operator T()

(我懒得在代码中检查这个。如果有人愿意做这项工作并展示一个工作示例,请在这里给我留言,我可能会投票。

当然,像往常一样,你需要问自己这样做是否值得。隐式转换随时可能使您出错。(我曾经认为有时候他们并不邪恶。但是有一天,我不得不删除我工作代码中的最后一个隐式转换,因为它引入了一个非常难以发现的错误,并发誓再也不使用它们了。
此外,必须拼写出类型的额外安全性可以起到安全带的作用(防止你在严重失败时压碎挡风玻璃)。最后,c++ 11允许这样做:

auto foo = bar.get<int>(); // available today at a compiler near you

看,这个类型只拼一次。:)

您当然可以这样做,但可能不值得这样做。一种方法是提供一个静态函数,将可变模板实例成员转换为boost::any,并具有一个静态函数表:

template <...> class any_tuple 
{
...
    typedef boost::any (*extract_member)(any_tuple &);
    static extract_member member_extrators[...];
    boost::any Get(int index) { return member_extractors[index](*this); }
};

但是你需要担心初始化和其他的事情