可变参数模板容器类中是否可以使用无模板 Getter?

发布于 2024-12-04 20:18:40 字数 415 浏览 0 评论 0原文

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

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

这很不方便,并且可能会出现错误。我忍不住觉得有一种方法可以构造该类,因此您不必显式指定它。

int MyInt = MyTuple.Get(0);

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

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

A hypothetical variadic template tuple class would, as far as I can tell, have to use getters with template parameters.

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

This is inconvenient, and introduces potential for error. I can't help but feel that there's a way to construct the class so you you don't have to explicitly specify it.

int MyInt = MyTuple.Get(0);

My first thought was for the Get() member function to return another class that figures out the type on its own, probably by comparing typeid(Foo).name() to values in some precomputed list. That still has to happen before runtime, though, and I couldn't figure out a way to iterate through anything like that at compile-time.

Is there any way for a variadic template container class - like a tuple - to have a getter that doesn't require the type to be explicitly specified?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

岛歌少女 2024-12-11 20:18:40

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

ETC。

You mean like std::tuple?

The getter's template argument specifies the index of the member, not the type. By definition, the number and types of a tuple are fixed at compile time. Since the type depends on the index, and the type must be known at compile time, the getter must be templated.

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; }

etc.

愛放△進行李 2024-12-11 20:18:40

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

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

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

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

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

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

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

The rules of C++ do not generally allow what you're asking for.

A struct is a collection of values of known types, which are accessed by a name which must be provided at compile time. That last part is crucial, because it allows the C++ compiler to know which value you're talking about.

A tuple is a collection of values of known types, which are accessed by a number. However, just because it's a number does not mean it can be a runtime-defined number, just as having a string name for a member of a struct does not mean you can get that member at runtime.

Each member of the tuple is typed, and C++ needs to know the types of things at compile time in order to function. Similarly, the return type of any function is known at compile time; it cannot change based on the value of a parameter. You can select a different function based on the type (function overloading or template argument deduction), but it cannot be based on a value, because values (unless they're compile-time constants) are only known at runtime.

In order to do what you want, you will have to break the C++ type system in some way. For example, you could theoretically have a get function which returned a typeless pointer. Or, if you want at least some pretense of type-safety, perhaps a boost::any, or a boost::variant of the tuple's types. But that's about the best you're going to be able to do.

Even if you specify the type at compile time (get<Type>), that's still not good enough due to the possibility of a type mismatch. What do you do then?

The concept is just not going to really work in a compile-time typed language like C++.

我们只是彼此的过ke 2024-12-11 20:18:40

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

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

MyTuple.Get(0); what would be the return type of such Get declaration? If you are okey with it returning a variant over every possible type in the tuple, then you could make it work, yes.

On the other hand, Boost.Tuple and the C++0x Tuple take the index as a template parameter. No potential for mistakes there. Check them out.

林空鹿饮溪 2024-12-11 20:18:40

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

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

The standard library includes std::get function template that works with std::tuple and std::pair and doesn't require the type to be explicity specified. It works by taking the index as a template parameter.

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*

The return type must always be known at compile-time, and it depends on the index, so you cannot possibly take the index as a runtime argument.

把梦留给海 2024-12-11 20:18:40

这不是假设。 boost::tuple 的行为如您所描述的那样。

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

It's not hypothetical. boost::tuple behaves the way you describe.

boost::tuple<int, float> myTuple = boost::make_tuple(10, 3.1);
int myInt = myTuple.Get<0>();
空心空情空意 2024-12-11 20:18:40

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

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

template<typename T> operator T()

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

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

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

看,类型只拼写一次。 <代码>:)

So you already got a few replies explaining that this would work if you pass the index as a compile-time parameter. I want to comment on the general idea, though:

I think I have seen the trick to return a proxy that has a templatized implicit conversion operator:

template<typename T> operator T()

(I'm too lazy to check this in code, though. If somebody is willing to do the work and show a working example, drop me a comment here and I'll likely upvote.)

Of course, as always with such hacks, you need to ask yourself whether it's worth it. Implicit conversion can trip you up any time. (I used to think that sometimes they are not evil. But someday I had to remove the last implicit conversion in working code of mine, because it had introduced a very hard to find bug, and swore to never to use them again.)
Also, the additional safety of having to spell out the type can act as seat belt (preventing you from crushing through the windshield when you fail badly).
Finally, C++11 allows this:

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

See, the type is only spelled out once. :)

太阳男子 2024-12-11 20:18:40

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

但是随后您必须担心初始化和所有问题那个东西。

You certainly could do it, but it probably isn't worth it. One way you can do it is to supply a static function that converts the variadic template instance member to a boost::any and have a static table of functions:

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); }
};

But then you have to worry about initialization and all that stuff.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文