变量模板容器类中是否可以使用无模板的getter?

时间:2011-09-15 23:14:25

标签: c++ oop c++11 variadic-templates

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

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

这很不方便,并且引入了错误的可能性。我不禁觉得有一种方法来构建类,所以你不必明确指定它。

int MyInt = MyTuple.Get(0);

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

对于可变参数模板容器类(如元组),有没有办法让getter不需要显式指定类型?

7 个答案:

答案 0 :(得分:7)

你的意思是std::tuple

getter的template参数指定成员的索引,而不是类型。根据定义,元组的数量和类型在编译时是固定的。由于类型取决于索引,并且必须在编译时知道类型,因此必须模板化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; }

答案 1 :(得分:3)

C ++的规则通常不允许您要求的内容。

struct是已知类型的值的集合,可以通过必须在编译时提供的名称来访问。最后一部分是至关重要的,因为它允许C ++编译器知道您正在谈论的是哪个值。

tuple是已知类型值的集合,可通过数字访问。但是,仅仅因为它是一个数字并不意味着它可以是运行时定义的数字,就像拥有结构成员的字符串名称并不意味着你可以在运行时获得该成员。

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

为了做你想做的事,你必须以某种方式打破C ++类型系统。例如,理论上你可以有一个get函数返回一个无类型指针。或者,如果你想要至少一些类型安全的假装,可能是boost::anyboost::variant类型的tuple。但那是你能做的最好的事情。

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

这个概念在C ++这样的编译时类型语言中无法真正发挥作用。

答案 2 :(得分:1)

MyTuple.Get(0);这种Get声明的返回类型是什么?如果你是okey,它会在元组中的每个可能的类型上返回一个变体,那么你可以让它工作,是的。

另一方面,Boost.Tuple和C ++ 0x Tuple将索引作为模板参数。没有潜在的错误。检查一下。

答案 3 :(得分:1)

这不是假设。 boost :: tuple的行为与你描述的方式相同。

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

答案 4 :(得分:1)

标准库包含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*

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

答案 5 :(得分:1)

所以你已经得到了一些回复,解释说如果你将索引作为编译时参数传递,这将会有效。不过,我想评论一般的想法:

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

template<typename T> operator T()

(我懒得在代码中检查这个。如果有人愿意做这个工作并展示一个有效的例子,请在这里给我发表评论,我可能会投票。)

当然,就像这些黑客一样,你需要问自己是否值得。隐式转换可以随时绊倒你。 (我曾经认为有时它们不是邪恶的。但总有一天我不得不删除我工作代码中的最后一次隐式转换,因为它引入了一个非常难以找到的bug,并发誓永远不会再次使用它们。)
此外,必须拼出该类型的额外安全性可以充当安全带(防止您在严重失效时通过挡风玻璃挤压)。
最后,C ++ 11允许这样:

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

请参阅,该类型仅拼写一次。 :)

答案 6 :(得分:0)

你当然可以做到,但它可能不值得。一种方法是提供一个静态函数,将可变参数模板实例成员转换为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); }
};

但是你必须担心初始化和所有这些。