这个问题让我思考。有时,如果无法定义参数的公共 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
。它的声明没有提及元组。
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
?
发布评论
评论(1)
我不明白这一点。你是什么意思?
对于一般情况来说这是不可能的。想象一下这个
快乐的宏元编程:)
两个部分专业化的部分排序
就像将它们转换为函数模板并排序它们
一样,像您的分析所说的部分排序正确地为每个模板参数放置唯一类型,将其转换为参数推导的参数,将其与其他模板
如果存在非推导的上下文,则不会像平常一样进行比较。就像如果
BmM
是typename Foo::X
一样,上面的第一个推导会将最后一个子类型的推导视为成功,因为非推导的子类型不能不匹配语境。但反过来说,如果AmM
不是非推导上下文,则Foo::X
与AmM
可能 不匹配本身。如果一轮推导成功,而不是相反(...省略一些其他规则,因为它们只发生在实际函数模板排序中),则推导失败的一轮上方右侧的模板更加专业。否则,部分特化是无序的。
I don't understand this. What do you mean?
It's impossible to do so for the general case. Imagine this one
Happy macro meta-programming :)
Partial ordering for two partial specializations
Works like transforming them to function templates and ordering them
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
If there is a non-deduced context it's not compared like usual. Like if
BmM
istypename 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
againstAmM
can mismatch though ifAmM
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.