【问题标题】:May std::tuple_element double as a universal template argument retriever?std::tuple_element 可以兼作通用模板参数检索器吗?
【发布时间】:2010-09-13 09:03:24
【问题描述】:

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

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

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

但是tuple_element 没有和tuple 结婚;它也适用于pair 和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 的第二个参数是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 是否也可以实现这样的特化。对调角色重复,如果得到相反的结果,则认为一名候选人更专业。

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

例如,这是一个非常通用的用户声明的专业化。它为包含给定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, ...可以被一般情况处理,但用户的情况不能处理完全由人工唯一类型组成的序列,因为它不会包含user_type。

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

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


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

【问题讨论】:

  • 所以您的问题或多或少是,考虑到tuple_element 的要求,库实现者是否可以像您一样提供通用的模板参数解包器,并且不会破坏任何有效的用户专业化...?
  • @Doug:差不多,是的 :v)。

标签: c++ templates c++11 standard-library partial-specialization


【解决方案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>

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

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

【讨论】:

  • 您粘贴的报价只是设置问题。如果一个实现破坏了用户的专业化,那么它就是不合格的。相反,如果它从不干扰,我相信这个功能会符合要求。
  • 只是使用术语“通用模板索引器”来指代可以存在的东西;v) ... 所以,它总是在用户专业化之后排序,或者不?是否存在仅在非推断上下文中提及其类型的有效用户专业化?如果是这样,它会根据 17.6.3.2.1/1“取决于用户定义的类型”吗?还是需要澄清这个术语?
  • (结论:仅在非推导上下文中提及用户类的模板不依赖于用户定义的类型。)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-02-23
  • 1970-01-01
  • 1970-01-01
  • 2017-10-10
  • 2023-03-07
  • 2017-08-06
相关资源
最近更新 更多