3

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

C++0x 解决了typedef一个特定可变参数模板的问题,tuple.

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

但未tuple_element结婚tuple;它也适用于pairand array。它的声明没有提到tuple.

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

但它不需要。模板模板参数可以匹配tuple,以及仅在类型上参数化的任何其他模板。

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' 的第二个参数是 a size_t,而不是类型。


不幸的是,允许用户tuple_element为他们自己的类型专门化接口。用户的先决条件及其保证由 C++0x §17.6.3.2.1/1 给出:

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

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

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

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

通过用唯一的虚拟类型替换 A 中的部分特化参数来检查通用性,并检查 B 是否也可以实现这样的特化。以相反的角色重复,如果获得相反的结果,则已知一名候选人更专业。

用户特化中用户定义类型的存在保证了它的优先级,因为在参数-getter 中必须有一个与之不匹配的唯一虚拟类型。

例如,这是一个非常通用的用户声明的专业化。它tuple_element为包含给定的任何类型参数化模板定义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 ... > >;

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

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

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


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

4

1 回答 1

1

因此,一般特化不仅不能与数组特化冲突,而且不能与任何命名用户定义类型的特化冲突。也就是说,如果用户声明了一个特化,通用参数 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>

如果存在未推断的上下文,则不会像往常一样进行比较。就像 if BmMis一样typename Foo::X,上面的第一个推论会将最后一个子类型的推论视为成功,因为对于非推导的上下文不能存在不匹配。但反过来说,虽然 if本身不是非推导上下文,但Foo::X反对AmM 可能不匹配。AmM

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

于 2010-09-13T11:50:22.150 回答