【发布时间】:2017-08-19 09:33:23
【问题描述】:
identity 模板是关闭某个(依赖)模板参数的演绎的常用习惯用法,例如允许隐式转换,如下例所示:
#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);
}
但是不同编译器的结果不一样:Live demo on Godbolt
- GCC 6:
ok1ok2 - clang 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 中工作的问题:
test(a, b)如何选择#1而test(b, c)-#2?至少在test(a, b)的情况下,它们看起来同样出色。为什么编译器不抱怨两个签名完全相同的
test实例化?
【问题讨论】:
-
大约 2。-我猜这是因为“第一个匹配模板获胜”的逻辑。但这并不能解释 1. 根本......
-
(1.) 它们并不一样好。否则编译器会发出模棱两可的信号。
test(a, b)将选择 #1,因为a和b在 is-a 关系中都是A<X>,而 #1 是完全匹配的。 #2 也是完全匹配的,但需要更多步骤。 -
昨天在a related question 上,协议好像是MSVC没有完全实现偏序。