1

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

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

这是不方便的,并且引入了错误的可能性。我不禁觉得有一种方法可以构造类,因此您不必显式指定它。

int MyInt = MyTuple.Get(0);

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

可变参数模板容器类(例如元组)有什么方法可以拥有不需要显式指定类型的 getter?

4

7 回答 7

7

你的意思是像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; }

等等

于 2011-09-15T23:18:18.130 回答
3

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

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

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

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

为了做你想做的事,你必须以某种方式打破 C++ 类型系统。例如,理论上你可以有一个get返回无类型指针的函数。或者,如果您至少想要某种类型安全的伪装,也许是 aboost::any或 aboost::varianttuple类型。但这是你能做到的最好的。

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

这个概念在像 C++ 这样的编译时类型语言中并不会真正起作用。

于 2011-09-16T00:53:20.457 回答
1

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

另一方面,Boost.Tuple 和 C++0x Tuple 将索引作为模板参数。那里没有出错的可能性。去看一下。

于 2011-09-15T23:17:43.763 回答
1

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

boost::tuple<int, float> myTuple = boost::make_tuple(10, 3.1);
int myInt = myTuple.Get<0>();
于 2011-09-15T23:17:51.307 回答
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*

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

于 2011-09-15T23:19:05.323 回答
1

因此,您已经收到了一些回复,解释说如果您将索引作为编译时参数传递,这将起作用。不过,我想评论一下总体思路:

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

template<typename T> operator T()

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

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

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

看,类型只拼写一次。:)

于 2011-09-16T06:50:43.007 回答
0

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

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

于 2011-09-16T00:18:17.680 回答