功能模板的部分排序和非推导上下文在MSVC 2017中不起作用 [英] Partial ordering of function templates and non-deduced context not working in MSVC 2017

查看:42
本文介绍了功能模板的部分排序和非推导上下文在MSVC 2017中不起作用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

identity 模板是一种常见的惯用法,用于关闭某些(从属)模板参数的推导,例如,允许隐式转换,如下面的示例所示:

The identity template is a common idiom for switching off deduction for a certain (dependent) template argument, for example to allow for implicit conversions like in the example below:

#include <iostream>

template<class T>
struct A {};
struct B : public A<int> {};
struct C {
  operator B() { return {}; }
};

template<typename U> struct identity { typedef U type; };

template<class T> using identity_t = typename identity<T>::type;

template<class X>
void test(A<X> arg1, A<X> arg2) {              // #1
  std::cout << "ok1";
}

template<class X>
void test(A<X> arg1, identity_t<A<X>> arg2) {  // #2
  std::cout << "ok2";
}

int main() {
  B a, b;
  C c;
  test(a, b);
  test(b, c);
}

但是来自不同编译器的结果并不相同: Godbolt上的实时演示

But the results from different compilers are not the same: Live demo on Godbolt


  • GCC 6: ok1ok2

  • c 5: ok1ok2

  • MSVC 2017:


27 : <source>(27): error C2668: 'test': ambiguous call to overloaded function
20 : <source>(20): note: could be 'void test<int>(A<int>,A<int>)'
15 : <source>(15): note: or       'void test<int>(A<int>,A<int>)'
27 : <source>(27): note: while trying to match the argument list '(B, B)'


这种错误类型是有道理的(尽管肯定是MSVC中的一个错误),因此带给我们有关为什么和它在GCC和clang中的工作方式

The error kind of makes sense (although is surely a bug in MSVC) and thus brings us to my questions about why and how it works in GCC and clang:


  1. 如何 test(a,b)是否选择#1 test(b,c)-#2 ?至少在 test(a,b)的情况下,它们看起来像是同样好的候选人。

  1. How does test(a, b) select #1 while test(b, c) - #2? They look like equally good candidates at least in case test(a, b).

为什么没有编译器不是抱怨两个完全相同签名的 test 实例吗?

Why isn't the compiler complaining about two test instantiations with an exact same signature?


推荐答案

这是MSVC中的错误,程序是正确的。

It's a bug in MSVC, the program is correct.


test(a,b)如何选择#1

对于 test(a,b),重载解析从函数调用(请参见 [temp.deduct.call] ):

For test(a, b), overload resolution performs argument deduction from a function call (see [temp.deduct.call]):


  • #1推导为 void test(A< int< A< int>))

  • #2被推导出为 void test(A< int> ;,< non-deduced context>) ,然后从arg1中将arg2合成为 A ,结果: void test(A ,A&l t; int>)

  • #1 is deduced as void test(A<int>, A<int>)
  • #2 is deduced as void test(A<int>, <non-deduced context>), arg2 is then synthesized from arg1 as A<int>, the result: void test(A<int>, A<int>)

有不止一种可行的选择,因此该过程以部分订购(请参见 [temp.deduct.partial] )。

There is more than one viable alternative, so the process continues with partial ordering (see [temp.deduct.partial]).

部分排序使用原始模板,尝试从类型中扣除 (< a href = https://timsong-cpp.github.io/cppwp/n3337/temp.deduct.type rel = nofollow noreferrer> [temp.deduct.type] )将一个模板的参数转换为另一个模板(经过较小的转换后),反之亦然。
如果扣除仅在一个方向上成功,则获胜模板将被选为最专业的。

Partial ordering uses the original templates, trying a deduction from a type ([temp.deduct.type]) pair-wise of each argument of one template into another (after a minor conversion), and vice versa. If the deduction succeeds in only one direction, the winning template is selected as the most specialized.

类型的扣除总是失败在嵌套上下文中(范围运算符 :: 左侧的任何内容都是嵌套上下文),请参见 [temp.deduct.type] / 5

Deduction from a type always fails at nested contexts (anything to the left of the scope operator :: is a nested context), see [temp.deduct.type]/5:


非推论上下文为:

The non-deduced contexts are:

嵌套名称-使用 -id rel = nofollow noreferrer>合格ID

— The nested-name-specifier of a type that was specified using a qualified-id.

。 。 。

因此,这意味着#2总是会丢失部分排序;推论总是失败的,反之则总是会成功的:

So this means that #2 will always lose in partial ordering; the deduction into it will always fail, whereas the other way will always succeed:


  1. 推论 void测试(A T,A T)来自 void测试(A U,类型名称标识A A U:>:类型):P1 = A< T> ,A1 = A< U> ,P2 = A ,A2 = A ,成功,T = U

  1. Deducing void test(A<T>,A<T>) from void test(A<U>, typename identity<A<U>>::type) : P1=A<T>, A1=A<U>, P2=A<U>, A2=A<U>, success, T=U

推导 void测试(A T,类型名称标识A T :: type) void测试(A ,A 的code>:P1 = A ,A1 = A ,P2 = ,失败

Deducing void test(A<T>, typename identity<A<T>>::type) from void test(A<U>,A<U>) : P1=A<T>, A1=A<U>, P2=<non-deduced-context>, fail

因此,部分排序的结果是:使用 void test(A< T>,A< T>)(#1)进行通话 test(a,b)

So the outcome of partial ordering is: use void test(A<T>,A<T>) (#1) for the call test(a, b).

test(b,c)如何选择#2

用于 test(b ,c) A< X> 不能从 C 推导(隐式转换为因此,#2是唯一可行的替代方案。 identity_t< A< X>> 在扣除到 A< int< ,因为已知 X (根据第一个参数推导)。

For test(b, c), A<X> cannot be deduced from C (implicit conversions are not considered during deduction), so #2 is the only viable alternative. identity_t<A<X>> is resolved after deduction to A<int>, since X is known (deduced from the first argument).


为什么编译器不抱怨具有完全相同签名的两个测试实例?

Why isn't the compiler complaining about two test instantiations with an exact same signature?

函数声明中引用的模板参数是实例化函数签名的一部分。参见 [temp.over.link]

The template parameters referenced in the function declaration are part of the signature of the instantiated function. See [temp.over.link]:



  1. 可以重载函数模板,以便两个不同的函数模板专长具有相同的类型。

  1. It is possible to overload function templates so that two different function template specializations have the same type.

此类专业化是不同的功能,并且不违反一定义规则。

Such specializations are distinct functions and do not violate the one-definition rule.


这篇关于功能模板的部分排序和非推导上下文在MSVC 2017中不起作用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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