具有非推导上下文的部分专业化排序 [英] partial specialization ordering with non-deduced context

查看:101
本文介绍了具有非推导上下文的部分专业化排序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

根据[temp.class.order]§14.5.5.2,在此示例中选择了t的部分专业化:

According to [temp.class.order] §14.5.5.2, the selection of a partial specialization of t in this example:

template< typename >
struct s { typedef void v, w; };

template< typename, typename = void >
struct t {};

template< typename c >
struct t< c, typename c::v > {};

template< typename c >
struct t< s< c >, typename s< c >::w > {};

t< s< int > > q;

在此示例中,

等效于选择f的重载:

is equivalent to the selection of an overload of f in this example:

template< typename >
struct s { typedef void v, w; };

template< typename, typename = void >
struct t {};

template< typename c >
constexpr int f( t< c, typename c::v > ) { return 1; }

template< typename c >
constexpr int f( t< s< c >, typename s< c >::w > ) { return 2; }

static_assert ( f( t< s< int > >() ) == 2, "" );

但是,GCC,Clang和ICC都拒绝第一个示例是模棱两可的,但是接受第二个示例.

However, GCC, Clang, and ICC all reject the first example as ambiguous, but accept the second.

更奇怪的是,如果将::v替换为::w,第一个示例将起作用,反之亦然.显然,未推导的上下文c::s< c >::在专业化排序中被考虑,这是没有道理的.

Even more strangely, the first example works if ::v is replaced with ::w or vice versa. The non-deduced contexts c:: and s< c >:: are apparently being considered in specialization ordering, which doesn't make sense.

我是否缺少标准中的某些内容,或者所有这些实现都有相同的错误?

Am I missing something in the standard, or do all these implementations have the same bug?

推荐答案

暂时切换到极端学究模式,是的,我认为您缺少标准中的某些内容,不,它不会有任何区别在这种情况下.

Switching to extremely-pedantic-mode for a moment, yes, I think you are missing something in the standard, and no, it shouldn't make any difference in this case.

所有标准参考均参考当前的工作草案N4527.

All standard references are to N4527, the current working draft.

[14.5.5.2p1]说:

[14.5.5.2p1] says:

对于两个类模板的部分专业知识,第一个是 more 比第二个要专业,因为以下内容重写为两个 功能模板,第一个功能模板更专业 根据功能模板的排序规则,比第二个 (14.5.6.2):

For two class template partial specializations, the first is more specialized than the second if, given the following rewrite to two function templates, the first function template is more specialized than the second according to the ordering rules for function templates (14.5.6.2):

  • 第一个功能模板的模板参数与第一个部分专业化的模板参数相同,并且具有一个功能参数,其 type是一个类模板专业化,其模板参数为 第一个部分专业化,并且
  • 第二个功能模板具有与第二个部分专业化相同的模板参数,并且具有单个功能参数 其类型是带有模板的专业类模板 第二部分专业化的论点.
  • the first function template has the same template parameters as the first partial specialization and has a single function parameter whose type is a class template specialization with the template arguments of the first partial specialization, and
  • the second function template has the same template parameters as the second partial specialization and has a single function parameter whose type is a class template specialization with the template arguments of the second partial specialization.

转到[14.5.6.2p1]:

Going to [14.5.6.2p1]:

[...] 部分排序重载的函数模板声明 在以下上下文中用于选择要使用的功能模板 功能模板专业化指的是:

[...] Partial ordering of overloaded function template declarations is used in the following contexts to select the function template to which a function template specialization refers:

  • 在重载解析期间调用函数模板特化(13.3.3);
  • 获取功能模板专门化的地址时;
  • 当操作者放置删除正在一个函数模板特殊化被选择以匹配操作者放置新的(3.7.4.2, 5.3.4);
  • 当朋友函数声明(14.5.4),显式实例化(14.7.2)或显式专业化(14.7.3)引用时 功能模板专业化.
  • during overload resolution for a call to a function template specialization (13.3.3);
  • when the address of a function template specialization is taken;
  • when a placement operator delete that is a function template specialization is selected to match a placement operator new (3.7.4.2, 5.3.4);
  • when a friend function declaration (14.5.4), an explicit instantiation (14.7.2) or an explicit specialization (14.7.3) refers to a function template specialization.

没有提及类模板专业化的部分排序.但是,[14.8.2.4p3]说:

No mention of partial ordering of class template specializations. However, [14.8.2.4p3] says:

用于确定排序的类型取决于上下文 部分排序已完成:

The types used to determine the ordering depend on the context in which the partial ordering is done:

  • 在函数调用的上下文中,使用的类型是函数调用具有参数的那些函数参数类型.
  • 在调用转换函数的上下文中,将使用转换函数模板的返回类型.
  • 在其他情况下(14.5.6.2),使用功能模板的功能类型.
  • In the context of a function call, the types used are those function parameter types for which the function call has arguments.
  • In the context of a call to a conversion function, the return types of the conversion function templates are used.
  • In other contexts (14.5.6.2) the function template’s function type is used.

即使它回顾了[14.5.6.2],也确实说了其他上下文".我只能得出结论,当将部分排序算法应用于根据[14.5.5.2]中的规则生成的功能模板时,使用的是功能模板的功能类型,而不是参数类型的列表,因为函数会发生这种情况称呼.

Even though it refers back to [14.5.6.2], it does say "other contexts". I can only conclude that, when applying the partial ordering algorithm to the function templates generated according to the rules in [14.5.5.2], the function template’s function type is used, not the list of parameter types, as it would happen for a function call.

因此,在您的第一个代码段中选择t的部分专业化将等同于不涉及函数调用的情况,而是等效于采用函数模板地址的情况(例如)在其他情况"下:

So, the selection of a partial specialization of t in your first snippet would be equivalent not to a case involving a function call, but to one that takes the address of a function template (for example), which also falls under "other contexts":

#include <iostream>

template<typename> struct s { typedef void v, w; };
template<typename, typename = void> struct t { };

template<typename C> void f(t<C, typename C::v>) { std::cout << "t<C, C::v>\n"; }
template<typename C> void f(t<s<C>, typename s<C>::w>) { std::cout << "t<s<C>, s<C>::w>\n"; }

int main()
{
   using pft = void (*)(t<s<int>>);
   pft p = f;
   p(t<s<int>>());
}

(由于我们仍处于极端学步模式,因此我重新编写了功能模板,就像[14.5.5.2p2]中的示例一样.)

(Since we're still in extremely-pedantic-mode, I rewrote the function templates exactly like the example in [14.5.5.2p2].)

不用说,它也会编译并打印t<s<C>, s<C>::w>.它产生不同行为的机会很小,但是我不得不尝试一下.考虑到算法的工作原理,如果函数参数是例如引用类型(在函数调用的情况下触发[14.8.2.4]中的特殊规则,而在其他情况下则不触发),则可能会有所不同,但是使用从类模板专业化生成的功能模板不会出现这种形式.

Needless to say, this also compiles and prints t<s<C>, s<C>::w>. The chances of it producing different behaviour were slim, but I had to try it. Considering how the algorithm works, it would have made a difference if the function parameters were, for example, reference types (triggering the special rules in [14.8.2.4] in the case of a function call, but not in the other cases), but such forms can't occur with function templates generated from class template specializations.

所以,整个弯路并没有帮助我们,但是...这是一个language-lawyer问题,我们必须在此处添加一些标准引号...

So, this whole detour didn't help us one bit, but... it's a language-lawyer question, we had to have some standard quotes in here...

与您的示例相关的一些活跃的核心问题:

There are some active Core issues related to your example:

  • 1157 包含我认为相关的说明:

  • 1157 contains a note that I think is relevant:

模板参数推导是尝试匹配P和推导的 A;但是,如果以下情况未将模板参数推导指定为失败 P和推导的A不兼容.这可能发生在 非推论上下文的存在.尽管有括号 14.8.2.4 [temp.deduct.partial]第9段,模板中的声明 参数推导可能会成功确定以下参数的模板参数 每个模板参数,同时生成推导的A,而不是 与相应的P兼容.

Template argument deduction is an attempt to match a P and a deduced A; however, template argument deduction is not specified to fail if the P and the deduced A are incompatible. This may occur in the presence of non-deduced contexts. Notwithstanding the parenthetical statement in 14.8.2.4 [temp.deduct.partial] paragraph 9, template argument deduction may succeed in determining a template argument for every template parameter while producing a deduced A that is not compatible with the corresponding P.

我不确定这是否明确指定;毕竟,[14.8.2.5p1]说

I'm not entirely sure that's so clearly specified; after all, [14.8.2.5p1] says

[...]查找在替换推论值[...]之后使P与A兼容的模板参数值[...].

[...] find template argument values [...] that will make P, after substitution of the deduced values [...], compatible with A.

和[14.8.2.4]全文引用[14.8.2.5].但是,很明显,当涉及到非推论上下文时,功能模板的部分排序不会寻找兼容性,而更改会破坏很多有效的情况,因此我认为这只是标准中缺乏适当的规范.

and [14.8.2.4] references [14.8.2.5] in its entirety. However, it's pretty clear that partial ordering of function templates doesn't look for compatibility when non-deduced contexts are involved, and changing that would break a lot of valid cases, so I think this is just a lack of proper specification in the standard.

在较小程度上, 1847 与模板专业化参数中出现的非推断上下文有关.它引用 1391 来解决该问题;我认为该措词存在一些问题-此答案中的更多详细信息.

To a lesser extent, 1847 has to do with non-deduced contexts appearing in arguments to template specializations. It references 1391 for the resolution; I think there are some issues with that wording - more details in this answer.

对我来说,所有这些都表明您的示例应该有效.

To me, all of this says that your example should work.

与您一样,三个不同的编译器中都存在相同的不一致之处,这让我很感兴趣.在我确认MSVC 14表现出与其他产品完全相同的行为后,我变得更加着迷.所以,当我有空的时候,我以为我会快速看一下Clang的功能.事实证明,它不是很快,但得出了一些答案.

Like you, I was quite intrigued by the fact that the same inconsistency is present in three different compilers. I got even more intrigued after I verified that MSVC 14 exhibits the exact same behaviour as the others. So, when I got some time, I thought I'd take a quick look at what Clang does; it turned out to be anything but quick, but it yielded some answers.

与我们的案例有关的所有代码都在 lib/Sema/SemaTemplateDeduction.cpp .

All the code relevant to our case is in lib/Sema/SemaTemplateDeduction.cpp.

推论算法的核心是 DeduceTemplateArgumentsByTypeMatch 函数;推论的所有变体最终都调用它,然后递归地使用它来遍历复合类型的结构,有时借助重载的DeduceTemplateArguments函数集和一些

The core of the deduction algorithm is the DeduceTemplateArgumentsByTypeMatch function; all variants of deduction end up calling it, and it's then used recursively to walk through the structure of compound types, sometimes with the help of the heavily overloaded DeduceTemplateArguments set of functions, and some flags to adjust the algorithm based on the specific type of deduction being done and the parts of a type's form being looked at.

关于此功能要注意的一个重要方面是,它严格处理推论,而不是替代.它比较类型形式,为出现在推导上下文中的模板参数推导模板参数值,并跳过非推导上下文.它所做的唯一另一项检查是验证模板参数的推导参数值是否一致.我已经在我上面提到的答案中写了更多有关Clang在部分排序过程中进行演绎的方式.

An important aspect to note regarding this function is that it handles strictly deduction, and not substitution. It compares type forms, deduces template argument values for template parameters that appear in deduced contexts, and skips non-deduced contexts. The only other check it does is verifying that deduced argument values for a template parameter are consistent. I've written some more about the way Clang does deduction during partial ordering in the answer I mentioned above.

对于功能模板的部分排序,算法从 Sema::getMoreSpecializedTemplate 成员函数,它使用 isAtLeastAsSpecializedAs 两次,在两个模板之间来回移动,并比较结果.

For partial ordering of function templates, the algorithm starts in the Sema::getMoreSpecializedTemplate member function, which uses a flag of type enum TPOC to determine the context for which partial ordering is being done; the enumerators are TPOC_Call, TPOC_Conversion, and TPOC_Other; self-explanatory. This function then calls isAtLeastAsSpecializedAs twice, back and forth between the two templates, and compares the results.

isAtLeastAsSpecializedAs 打开TPOC标志的值,在此基础上进行一些调整,最终直接或间接调用 Sema::TDK_Success isAtLeastAsSpecializedAs 仅执行一项检查,以验证用于部分排序的所有模板参数都具有值.如果那也很好,它会返回true.

isAtLeastAsSpecializedAs switches on the value of the TPOC flag, makes some adjustments based on that, and ends up calling, directly or indirectly, DeduceTemplateArgumentsByTypeMatch. If that returns Sema::TDK_Success, isAtLeastAsSpecializedAs does only one more check, to verify that all template parameters that are used for partial ordering have values. If that's good too, it returns true.

这是功能模板的部分排序.基于上一节中引用的段落,我期望对类模板专业化进行部分排序以调用

And that's partial ordering for function templates. Based on the paragraphs quoted in the previous section, I was expecting partial ordering for class template specializations to call Sema::getMoreSpecializedTemplate with suitably constructed function templates and a flag of TPOC_Other, and everything to flow naturally from there. If that were the case, your example should work. Surprise: that's not what happens.

类模板专业化的部分订购始于 Sema::getMoreSpecializedPartialSpecialization .作为优化(红色标志!),它不合成功能模板,而是使用

Partial ordering for class template specializations starts in Sema::getMoreSpecializedPartialSpecialization. As an optimization (red flag!), it doesn't synthesize function templates, but rather uses DeduceTemplateArgumentsByTypeMatch to do type deduction directly on the class template specializations themselves as the types of P and A. This is fine; after all, that's what the algorithm for function templates would end up doing anyway.

但是,如果在扣除过程中一切顺利,那么它将调用 FinishTemplateArgumentDeduction (类模板专业化的重载),它进行替换和其他检查,包括检查专业化的替换参数

However, if all goes well during deduction, it then calls FinishTemplateArgumentDeduction (the overload for class template specializations), which does substitution and other checks, including checking that the substituted arguments to the specialization are equivalent to the original ones. This would be fine if the code were checking whether a partial specialization matches a set of arguments, but is not fine during partial ordering, and, as far as I can tell, causes the problem with your example.

因此,看来理查德·科登(Richard Corden)关于发生的事情的假设是正确的,但我并不完全是确保这是故意的.在我看来,这更像是一个疏忽.我们如何以所有相同的方式结束所有编译器仍然是一个谜.

So, it seems that Richard Corden's assumption as to what happens is correct, but I'm not entirely sure this was intentional. This looks more like an oversight to me. How we ended up with all compilers behaving the same way remains a mystery.

我认为,删除对 Sema::getMoreSpecializedPartialSpecialization 不会造成任何损害,并且将恢复部分排序算法的一致性.无需进行其他检查(由完成) isAtLeastAsSpecializedAs ),所有模板参数都具有值,因为我们知道所有模板参数都可以从专业化的参数中推导出;如果不是这样,部分专业化将无法匹配,因此我们不会首先进行部分排序. (是否首先允许这样的局部专业化是

In my opinion, removing the two calls to FinishTemplateArgumentDeduction from Sema::getMoreSpecializedPartialSpecialization would do no harm, and would restore consistency to the partial ordering algorithm. There's no need for the additional check (done by isAtLeastAsSpecializedAs) that all template parameters have values either, as we know all template parameters are deducible from the specialization's arguments; if they weren't, the partial specialization would fail matching, so we wouldn't get to partial ordering in the first place. (Whether such partial specializations are allowed in the first place is the subject of issue 549. Clang issues a warning for such cases, MSVC and GCC issue an error. Anyway, not a problem.)

作为旁注,我认为所有这些都适用于也可以重载变量模板的专业化内容.

As a side note, I think all of this applies to the overload for variable template specializations as well.

不幸的是,我没有为Clang设置构建环境,因此目前无法测试此更改.

Unfortunately, I don't have a build environment set up for Clang, so I can't test this change at the moment.

这篇关于具有非推导上下文的部分专业化排序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆