模板部分排序 - 为什么部分扣除在这里成功 [英] Template partial ordering - why does partial deduction succeed here

查看:107
本文介绍了模板部分排序 - 为什么部分扣除在这里成功的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

考虑以下简单(在模板问题过程中)示例:

Consider the following simple (to the extent that template questions ever are) example:

#include <iostream>

template <typename T>
struct identity;

template <>
struct identity<int> {
    using type = int;
};

template<typename T> void bar(T, T ) { std::cout << "a\n"; }
template<typename T> void bar(T, typename identity<T>::type) { std::cout << "b\n"; }

int main ()
{
    bar(0, 0);
}

clang和gcc都打印a。根据[temp.deduct.partial]和[temp.func.order]中的规则,为了确定部分排序,我们需要合成一些唯一的类型。因此,我们有两次尝试扣减:

Both clang and gcc print "a" there. According to the rules in [temp.deduct.partial] and [temp.func.order], to determine partial ordering, we need to synthesize some unique types. So we have two attempts at deduction:

+---+-------------------------------+-------------------------------------------+
|   | Parameters                    | Arguments                                 |
+---+-------------------------------+-------------------------------------------+
| a | T, typename identity<T>::type | UniqueA, UniqueA                          |
| b | T, T                          | UniqueB, typename identity<UniqueB>::type |
+---+-------------------------------+-------------------------------------------+

根据 Richard Corden的回答,在b上扣除,表达式 typename identity< UniqueB> :: type 被视为类型,不进行求值。也就是说,这将被合成为如下:

For deduction on "b", according to Richard Corden's answer, the expression typename identity<UniqueB>::type is treated as a type and is not evaluated. That is, this will be synthesized as if it were:

+---+-------------------------------+--------------------+
|   | Parameters                    | Arguments          |
+---+-------------------------------+--------------------+
| a | T, typename identity<T>::type | UniqueA, UniqueA   |
| b | T, T                          | UniqueB, UniqueB_2 |
+---+-------------------------------+--------------------+

很明显,对b的扣除失败。这两种不同的类型,所以你不能推断 T

It's clear that deduction on "b" fails. Those are two different types so you cannot deduce T to both of them.

A 上的扣除应该失败。对于第一个参数,您将匹配 T == UniqueA 。第二个参数是一个非推导的上下文 - 所以不会扣除成功iff UniqueA 可以转换为 identity< UniqueA> :: type ?后者是一个替代失败,所以我不知道这个推导是否也能成功。

However, it seems to me that the deduction on A should fail. For the first argument, you'd match T == UniqueA. The second argument is a non-deduced context - so wouldn't that deduction succeed iff UniqueA were convertible to identity<UniqueA>::type? The latter is a substitution failure, so I don't see how this deduction could succeed either.

gcc和clang在这种情况下如何以及为什么更喜欢a重载?

How and why do gcc and clang prefer the "a" overload in this scenario?

推荐答案

正如在注释中讨论的,我相信在函数模板部分排序算法的几个方面在标准中是不清楚的或根本没有指定,这在您的示例中显示。

As discussed in the comments, I believe there are several aspects of the function template partial ordering algorithm that are unclear or not specified at all in the standard, and this shows in your example.

为了使事情更有趣,MSVC(我测试12和14)拒绝调用是模糊的。我认为标准中没有什么东西来确定哪个编译器是正确的,但我想我可能有一个线索,差异来自哪里;

To make things even more interesting, MSVC (I tested 12 and 14) rejects the call as ambiguous. I don't think there's anything in the standard to conclusively prove which compiler is right, but I think I might have a clue about where the difference comes from; there's a note about that below.

您的问题(和这一个

Your question (and this one) challenged me to do some more investigation into how things work. I decided to write this answer not because I consider it authoritative, but rather to organize the information I have found in one place (it wouldn't fit in comments). I hope it will be useful.

首先,提议的issue 1391 。我们在评论和聊天中广泛讨论。我认为,虽然它提供了一些澄清,它也引入了一些问题。它将[14.8.2.4p4]更改为(以粗体显示的新文本):

First, the proposed resolution for issue 1391. We discussed it extensively in comments and chat. I think that, while it does provide some clarification, it also introduces some issues. It changes [14.8.2.4p4] to (new text in bold):


上面从参数模板和$ b来自参数模板的$ b对应类型用作
P A 的类型。 如果特定的 P 不包含模板参数
参与模板参数扣除,

Each type nominated above from the parameter template and the corresponding type from the argument template are used as the types of P and A. If a particular P contains no template-parameters that participate in template argument deduction, that P is not used to determine the ordering.

不是一个好的在我看来,这个想法有几个原因:

Not a good idea in my opinion, for several reasons:


  • 如果 P ,它不包含任何模板参数,所以它不包含任何参与参数扣除,这将使粗体声明适用于它。然而,这将使 template< class T> f(T,int)模板< class T,class U> f(T,U)无序,这没有意义。

  • 它会影响用于确定排序的概念,这会影响[ 14.8.2.4p11]。这使得 template< class T> void f(T) template< class T> void f(typename A :: a)无序的(扣除成功从第一个到第二个,因为 T 用于根据新规则的部分排序,因此它可以保持没有值)。目前,我测试的所有编译器都报告第二个更专业。

  • 这将使#2 #1 在以下示例中:

  • If P is non-dependent, it doesn't contain any template parameters at all, so it doesn't contain any that participate in argument deduction either, which would make the bold statement apply to it. However, that would make template<class T> f(T, int) and template<class T, class U> f(T, U) unordered, which doesn't make sense. This is arguably a matter of interpretation of the wording, but it could cause confusion.
  • It messes with the notion of used to determine the ordering, which affects [14.8.2.4p11]. This makes template<class T> void f(T) and template<class T> void f(typename A<T>::a) unordered (deduction succeeds from first to second, because T is not used in a type used for partial ordering according to the new rule, so it can remain without a value). Currently, all compilers I've tested report the second as more specialized.
  • It would make #2 more specialized than #1 in the following example:

#include <iostream>

template<class T> struct A { using a = T; };

struct D { };
template<class T> struct B { B() = default; B(D) { } };
template<class T> struct C { C() = default; C(D) { } };

template<class T> void f(T, B<T>) { std::cout << "#1\n"; } // #1
template<class T> void f(T, C<typename A<T>::a>) { std::cout << "#2\n"; } // #2

int main()
{
   f<int>(1, D());
}

#2 的第二个参数不用于部分排序,因此扣除从#1 成功到#2 ,但不是其他方式)。

(#2's second parameter is not used for partial ordering, so deduction succeeds from #1 to #2 but not the other way around). Currently, the call is ambiguous, and should arguably remain so.

看看Clang部分排序算法的实现,下面是我认为标准文本可以改变以反映实际发生的情况。

After looking at Clang's implementation of the partial ordering algorithm, here's how I think the standard text could be changed to reflect what actually happens.

离开[p4],并在[p8]和[p9]之间添加以下内容:

Leave [p4] as it is and add the following between [p8] and [p9]:


对于 P / A 对:


  • 如果 P 是非依赖的,当且仅当 P
  • 将推导的模板参数替换为中出现的非推导上下文中的

  • 如果模板参数值已成功推导出所有模板参数
  • ,则不会执行 P
    code> P
    ,除非那些仅出现在非推导的上下文中,那么扣除被认为是成功的(即使在 P P / A 对的扣除过程结束时没有值。
  • If P is non-dependent, deduction is considered successful if and only if P and A are the same type.
  • Substitution of deduced template parameters into the non-deduced contexts appearing in P is not performed and does not affect the outcome of the deduction process.
  • If template argument values are successfully deduced for all template parameters of P except the ones that appear only in non-deduced contexts, then deduction is considered successful (even if some parameters used in P remain without a value at the end of the deduction process for that particular P / A pair).

注意:


  • 关于第二个项目符号:[14.8.2.5p1]谈到找到模板参数值,它将会替换推导出的值(称为 P 推导出 A ),与 A 兼容。这可能导致在部分排序期间实际发生的混乱;

  • 在某些情况下,MSVC似乎没有实现第三个项目符号。

  • 第二个和第三个要点也适用于 P 有如 A< T,typename U :: b> ,不包含在问题1391中的措辞。

  • About the second bullet point: [14.8.2.5p1] talks about finding template argument values that will make P, after substitution of the deduced values (call it the deduced A), compatible with A. This could cause confusion about what actually happens during partial ordering; there's no substitution going on.
  • MSVC doesn't seem to implement the third bullet point in some cases. See the next section for details.
  • The second and third bullet points are intented to also cover cases where P has forms like A<T, typename U::b>, which aren't covered by the wording in issue 1391.

将当前的[p10]更改为:

Change the current [p10] to:


功能模板 F 至少与函数模板一样专门
G 当且仅当:

Function template F is at least as specialized as function template G if and only if:


  • 对于用于确定排序的每对类型, F 的类型至少与 G F 作为参数模板执行扣除时,和 c> G 作为参数模板,在对所有类型的对完成
    的推导后,所有在
    G 用于确定排序有价值,那些
    值在所有类型对之间是一致的。

  • for each pair of types used to determine the ordering, the type from F is at least as specialized as the type from G, and,
  • when performing deduction using the transformed F as the argument template and G as the parameter template, after deduction is done for all pairs of types, all template parameters used in the types from G that are used to determine the ordering have values, and those values are consistent across all pairs of types.

F G 更专业 if F 至少与专门的
as G G F

F is more specialized than G if F is at least as specialized as G and G is not at least as specialized as F.

使整个当前的[p11]

Make the entire current [p11] a note.

(需要调整1391至[14.8.2.5p4]的分辨率所添加的注释 - 对[14.8.2.1]很好,但不是[14.8.2.4]。)

(The note added by the resolution of 1391 to [14.8.2.5p4] needs to be adjusted as well - it's fine for [14.8.2.1], but not for [14.8.2.4].)

对于MSVC,在某些情况下,它看起来像 P 需要在扣除期间接收值 P / A pair ,以便从 A P 的成功扣除。我认为这可能是导致你的例子和其他人实现分歧的原因,但我已经看到至少一个情况,上述似乎不适用,所以我不知道相信什么。

For MSVC, in some cases, it looks like all template parameters in P need to receive values during deduction for that specific P / A pair in order for deduction to succeed from A to P. I think this could be what causes implementation divergence in your example and others, but I've seen at least one case where the above doesn't seem to apply, so I'm not sure what to believe.

另一个上面的语句似乎适用的例子:改变 template< typename T> void bar(T,T) template< typename T,typename U> void bar(T,U)在你的示例中交换结果:调用在Clang和GCC中是不明确的,但在MSVC中解析为 b

Another example where the statement above does seem to apply: changing template<typename T> void bar(T, T) to template<typename T, typename U> void bar(T, U) in your example swaps results around: the call is ambiguous in Clang and GCC, but resolves to b in MSVC.

其中一个例子不包括:

#include <iostream>

template<class T> struct A { using a = T; };
template<class, class> struct B { };

template<class T, class U> void f(B<U, T>) { std::cout << "#1\n"; }
template<class T, class U> void f(B<U, typename A<T>::a>) { std::cout << "#2\n"; }

int main()
{
   f<int>(B<int, int>());
}

这将选择#2 在Clang和GCC中,如预期的,但MSVC拒绝该调用是模糊的;不知道为什么。

This selects #2 in Clang and GCC, as expected, but MSVC rejects the call as ambiguous; no idea why.

标准中描述的部分排序算法说到合成一个唯一类型,或类模板,以生成参数。 ang管理,通过...不合成任何东西。它只是使用依赖类型的原始形式(如声明的),并匹配它们两种方式。这是有道理的,因为替换合成类型不添加任何新信息。它不能改变 A 类型的形式,因为通常没有办法告诉被替换的表单可以解析的具体类型。

The partial ordering algorithm as described in the standard speaks of synthesizing a unique type, value, or class template in order to generate the arguments. Clang manages that by... not synthesizing anything. It just uses the original forms of the dependent types (as declared) and matches them both ways. This makes sense, as substituting the synthesized types doesn't add any new information. It can't change the forms of the A types, since there's generally no way to tell what concrete types the substituted forms could resolve to. The synthesized types are unknown, which makes them pretty similar to template parameters.

当遇到 P 时,这是一个非常类似于模板参数的合成类型。非推导的上下文,Clang的模板参数推导算法简单地跳过它,通过返回成功的特定步骤。这不仅发生在部分排序期间,而且发生在所有类型的扣除,而不仅仅发生在函数参数列表的顶层,而是在以复合类型的形式遇到未导出的上下文时递归。由于某种原因,我发现,第一次我看到它令人惊讶。考虑到它,它当然是有意义的,是根据标准( [...]不参与类型推导[...] 在[14.8.2.5p4] )。

When encountering a P that is a non-deduced context, Clang's template argument deduction algorithm simply skips it, by returning "success" for that particular step. This happens not only during partial ordering, but for all types of deductions, and not just at the top level in a function parameter list, but recursively whenever a non-deduced context is encountered in the form of a compound type. For some reason, I found that surprising the first time I saw it. Thinking about it, it does, of course, make sense, and is according to the standard ([...] does not participate in type deduction [...] in [14.8.2.5p4]).

这与 Richard Corden的评论一致以他的答案,但我不得不实际看到编译器代码来了解所有的暗示(不是他的答案的错误,

This is consistent with Richard Corden's comments to his answer, but I had to actually see the compiler code to understand all the implications (not a fault of his answer, but rather of my own - programmer thinking in code and all that).

我在此答案

这篇关于模板部分排序 - 为什么部分扣除在这里成功的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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