std::tuple_element 可以兼作通用模板参数检索器吗?

发布于 2024-09-18 16:30:36 字数 3054 浏览 7 评论 0 原文

这个问题让我思考。有时,如果无法定义参数的公共 typedef,则从类模板特化中获取实际参数会很有用。在 C++03 中,这要么是糟糕的模板设计,要么是相反的设计意图,而且并不特别常见。但是可变参数模板使得 typedef 覆盖变得不可能,所以最好有一个工具来解决这个问题,而无需额外的工作。

C++0x 解决了一种特定可变参数模板 tupletypedef 问题。

tuple_element< 2, tuple< char, short, int > >::type my_int; // nth element type

但是 tuple_element 并没有与 tuple 联姻;它也适用于pairarray。它的声明没有提及元组。

template< size_t index, typename something_like_a_tuple >
struct tuple_element; // general case is left incomplete, unimplemented

tuple 通过部分专业化相关:

template< size_t index, typename ... tuple_elements >
struct tuple_element< index, tuple< tuple_elements ... > > { // impl. in here

但它不需要如此。模板模板参数可以匹配元组,以及仅通过类型参数化的任何其他模板。

template< size_t index,
    template< typename ... > class template_over_types,
    typename ... types >
struct tuple_element< index, template_over_types< types ... > > {

这将允许

tuple_element< 1, almost_any_template< char, int > >::type my_int;
tuple_element< 0, pair< int, char > >::type another_int; // same specialization

并允许对 array 进行额外的专门化,

template< size_t index, typename element, size_t extent >
struct tuple_element< index, array< element, extent > > 
    { typedef element type; }

不会发生冲突,因为 array 的第二个参数是 size_t,而不是类型。


不幸的是,用户可以为自己的类型专门化 tuple_element 接口。用户的前提条件及其保证由 C++0x §17.6.3.2.1/1 给出:

仅当声明依赖于用户定义类型并且专门化满足原始模板的标准库要求并且未明确禁止时,程序才可以将任何标准库模板的模板专门化添加到命名空间 std。

因此,一般专门化不仅不能与数组专门化冲突,而且不能与命名用户定义类型的任何专门化冲突。也就是说,如果用户声明了一个专门化,则通用参数 getter 的存在不会影响它是否被选择。

当实例化中出现歧义时(即两个部分特化与参数列表匹配),将比较备选方案以确定哪个是最专业的,换句话说,哪个是最不通用的。将备选方案称为 A 和 B。这意味着,如果 A 可以完成 B 的工作,但 B 不能完成 A 的工作,则 B 更专业。 A是多面手。将选择B。不考虑引发实例化的实际参数,因为已知它们与两个候选者都匹配。

由于我们希望通用模板遵循其他所有内容,因此我们的状态良好。

通过用唯一的虚拟类型替换 A 中的部分特化参数来检查通用性,并检查 B 是否也可以实现这样的特化。将角色互换,重复上述操作,如果得到相反的结果,则表明一位候选人更专业。

用户特化中用户定义类型的存在保证了其优先级,因为在参数获取器中必须有一个与其不匹配的相应唯一虚拟类型。

例如,这是一个非常通用的用户声明的专业化。它为任何包含给定user_type 的类型参数化模板定义tuple_element

 template< size_t I,
           template< typename ... > class any_template,
           typename ... T, typename ... U >
 struct tuple_element< I, any_template< T ..., ::user_type, U ... > >;

序列 ..., user_type, ... 可以由一般情况处理,但用户情况无法处理完全由人工唯一类型组成的序列,因为它不会包含 用户类型

如果任何用户专业化是候选者,那么它将是更优秀的。

(该标准确实在伪代码中指定了 tuple 的单独部分特化,但在 as-if 规则下可以省略。无论如何,如果实现,它将被相同的优先规则覆盖因为可以保护用户。)


我没有多次尝试部分排序规则。这个分析正确吗?实现通过 std::tuple_element 公开通用模板索引器是否可以?

This question got me thinking. Sometimes it's useful to grab an actual argument from a class template specialization, if it fails to define a public typedef of the argument. In C++03 it's a sign of either bad template design, or contrary design intent, and not particularly common. But variadic templates make typedef coverage impossible, so it would be nice to have a tool around to solve the problem without additional work.

C++0x solves the typedef problem for one particular variadic template, tuple.

tuple_element< 2, tuple< char, short, int > >::type my_int; // nth element type

But tuple_element isn't married to tuple; it also works with pair and array. Its declaration doesn't mention tuple.

template< size_t index, typename something_like_a_tuple >
struct tuple_element; // general case is left incomplete, unimplemented

tuple is related by a partial specialization:

template< size_t index, typename ... tuple_elements >
struct tuple_element< index, tuple< tuple_elements ... > > { // impl. in here

But it doesn't need to be. A template template parameter could match tuple, along any other template parameterized only over types.

template< size_t index,
    template< typename ... > class template_over_types,
    typename ... types >
struct tuple_element< index, template_over_types< types ... > > {

This would allow

tuple_element< 1, almost_any_template< char, int > >::type my_int;
tuple_element< 0, pair< int, char > >::type another_int; // same specialization

and yet allow the additional specialization for array

template< size_t index, typename element, size_t extent >
struct tuple_element< index, array< element, extent > > 
    { typedef element type; }

No conflict is possible because array's second argument is a size_t, not a type.


Unfortunately, the user is allowed to specialize the tuple_element interface for their own types. The user's precondition and their guarantee is given by C++0x §17.6.3.2.1/1:

A program may add a template specialization for any standard library template to namespace std only if the declaration depends on a user-defined type and the specialization meets the standard library requirements for the original template and is not explicitly prohibited.

So, not only must the general specialization not conflict with the array specialization, it can't conflict with any specialization that names a user-defined type. That is, if the user declares a specialization, the existence of the general argument getter cannot affect whether it's chosen.

When ambiguity arises in instantiation (that is, two partial specializations match an argument list), the alternatives are compared to determine which is most specialized, in other words least generalized. Call the alternatives A and B. This means that, if A can do B's job, but B cannot do A's job, then B is more specialized. A is the generalist. B will be chosen. The actual arguments instigating the instantiation aren't considered, since they are already known to be a match to both candidates.

Since we want the general template to defer to everything else, we're in good shape.

Generality is checked by replacing the partial specialization parameters in A with unique dummy types, and checking whether B can also implement such a specialization. Repeat with the roles reversed, and if the opposite result is obtained, one candidate is known to be more specialized.

The existence of a user-defined type in the user's specialization guarantees its precedence, because there must be a corresponding unique dummy type in the argument-getter which won't match it.

For example, here is a very general user-declared specialization. It defines tuple_element for any type-parameterized template containing a given user_type.

 template< size_t I,
           template< typename ... > class any_template,
           typename ... T, typename ... U >
 struct tuple_element< I, any_template< T ..., ::user_type, U ... > >;

The sequence ..., user_type, ... can be handled by the general case, but the user's case cannot handle a sequence composed entirely of artificial unique types, because it won't include user_type.

If any user specialization is a candidate, it will be the superior one.

(The standard does specify in pseudocode a separate partial specialization for tuple, but it could be omitted under the as-if rule. Anyway, if implemented, it would be covered by the same precedence rule as protects the user.)


I haven't made many forays into the partial ordering rules. Is this analysis correct? Is it OK for the implementation to expose a general template indexer through std::tuple_element?

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

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

发布评论

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

评论(1

影子是时光的心 2024-09-25 16:30:36

因此,通用专门化不仅不能与数组专门化冲突,而且不能与命名用户定义类型的任何专门化冲突。也就是说,如果用户声明了一个特化,通用参数 getter 的存在不会影响它是否被选择。

我不明白这一点。你是什​​么意思?


实现通过 std::tuple_element 公开通用模板索引器是否可以?

对于一般情况来说这是不可能的。想象一下这个

template<int A, char B, long C, class D, int &X, int(*Handler)()>
struct funny_template { };

int x, y();
std::tuple_element<3, funny_template<1, 2, 3, long, x, y> >::type along = 0;

快乐的宏元编程:)


我还没有对部分排序规则进行太多尝试。这个分析正确吗?

两个部分专业化的部分排序

template<class A1, ..., class AN>
class T<Am1, ..., AmM>;

template<class B1, ..., class BL>
class T<Bm1, ..., BmM>;

就像将它们转换为函数模板并排序它们

template<class A1, ..., class AN>
void f(T1<Am1, ..., AmM>);

template<class B1, ..., class BL>
void f(T2<Bm1, ..., BmM>);

一样,像您的分析所说的部分排序正确地为每个模板参数放置唯一类型,将其转换为参数推导的参数,将其与其他模板

T1A<Uam1, ..., UAmM> -> T2<Bm1, ..., BmM>
T2A<UBm1, ..., UBmM> -> T1<Am1, ..., AmM>

如果存在非推导的上下文,则不会像平常一样进行比较。就像如果 BmMtypename Foo::X 一样,上面的第一个推导会将最后一个子类型的推导视为成功,因为非推导的子类型不能不匹配语境。但反过来说,如果 AmM 不是非推导上下文,则 Foo::XAmM 可能 不匹配本身。

如果一轮推导成功,而不是相反(...省略一些其他规则,因为它们只发生在实际函数模板排序中),则推导失败的一轮上方右侧的模板更加专业。否则,部分特化是无序的。

So, not only must the general specialization not conflict with the array specialization, it can't conflict with any specialization that names a user-defined type. That is, if the user declares a specialization, the existence of the general argument getter cannot affect whether it's chosen.

I don't understand this. What do you mean?


Is it OK for the implementation to expose a general template indexer through std::tuple_element?

It's impossible to do so for the general case. Imagine this one

template<int A, char B, long C, class D, int &X, int(*Handler)()>
struct funny_template { };

int x, y();
std::tuple_element<3, funny_template<1, 2, 3, long, x, y> >::type along = 0;

Happy macro meta-programming :)


I haven't made many forays into the partial ordering rules. Is this analysis correct?

Partial ordering for two partial specializations

template<class A1, ..., class AN>
class T<Am1, ..., AmM>;

template<class B1, ..., class BL>
class T<Bm1, ..., BmM>;

Works like transforming them to function templates and ordering them

template<class A1, ..., class AN>
void f(T1<Am1, ..., AmM>);

template<class B1, ..., class BL>
void f(T2<Bm1, ..., BmM>);

Partial ordering like your analysis says correctly puts unique types for each template parameter transforming it to an argument for argument deduction, comparing it to the other template

T1A<Uam1, ..., UAmM> -> T2<Bm1, ..., BmM>
T2A<UBm1, ..., UBmM> -> T1<Am1, ..., AmM>

If there is a non-deduced context it's not compared like usual. Like if BmM is typename Foo::X, the first deduction above would consider deduction of the last sub-type as success because there can't be a mismatch for a nondeduced context. The other way around though, Foo::X against AmM can mismatch though if AmM is not a nondeduced context itself.

If deduction succeeds for one round and not the other way around (... leaving off some other rules because they only happen for real function template ordering), the template on the right side above for the round that failed deduction is more specialized. Otherwise, the partial specializations are unordered.

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