阅读问题如何使这些 std::function 参数明确?,到目前为止,我以为我理解了函数模板的偏序是什么,但是在阅读了那个问题之后,我写下了三个例子来检查编译器的行为,得到的结果让我很难理解。
示例 #1
template <class T>
void foo(T) {}
template <class T>
void foo(T&) {}
int main()
{
int i;
foo<int>(i); // error: call is ambiguous!
}
问题:这两个功能都是可行的,这很明显,但不是T&
更专业T
吗?相反,编译器会引发模棱两可的调用错误。
示例 #2
#include <iostream>
template <class T>
struct X {};
template <>
struct X<int>
{
X() {}
X(X<int&> const&) {} // X<int> is constructible from X<int&>
// note: this is not a copy constructor!
};
template <>
struct X<int&>
{
X() {}
X(X<int> const&) {} // X<int&> is constructible from X<int>
// note: this is not a copy constructor!
};
template <class T>
void bar(X<T>) { std::cout << __PRETTY_FUNCTION__ << std::endl; }
template <class T>
void bar(X<T&>) { std::cout << __PRETTY_FUNCTION__ << std::endl; }
int main()
{
bar<int>(X<int>()); // calls void bar(X<T>) [with T = int]
bar<int>(X<int&>()); // calls void bar(X<T&>) [with T = int]
}
问题:如果示例 #1 中的T&
andT
是不明确的,那么为什么这里没有一个 call 是不明确的?X<int>
可构造自X<int&>
,也X<int&>
可构造自 ,X<int>
这要归功于提供的构造函数。是因为编译器生成X<int>::X(X<int> const&)
的复制构造函数比,更好的转换序列X<int>::X(X<int&> const&)
(如果是这样,是什么让它更好,请注意参数是按值传递的),所以特化的顺序根本不重要?
示例#3
#include <iostream>
// note: a new type used in constructors!
template <class U>
struct F {};
template <class T>
struct X
{
X() {}
template <class U>
X(F<U> const&) {} // X<T> is constructible from any F<U>
// note: it takes F type, not X!
};
template <class T>
void qux(X<T>) { std::cout << __PRETTY_FUNCTION__ << std::endl; }
template <class T>
void qux(X<T&>) { std::cout << __PRETTY_FUNCTION__ << std::endl; }
int main()
{
qux<int>(F<int>()); // calls void qux(X<T&>) [with T = int]
qux<int>(F<int&>()); // calls void qux(X<T&>) [with T = int]
}
问题:现在这类似于“将 lambda[](int){}
与std::function<void(int&)>
和std::function<void(int)>
”从问题链接中匹配。为什么在这两个调用中都选择了更专业的函数模板?是不是因为转换顺序相同,所以偏序开始很重要?
在 GCC 4.9.0 上完成的所有测试都-std=c++11
没有额外的标志。