【发布时间】:2014-10-11 18:23:37
【问题描述】:
在阅读了How to make these std::function parameters unambiguous? 的问题后,我完全糊涂了,到目前为止,我以为我理解了 函数模板的部分排序 是什么,但在阅读了该问题后,我写下了三个示例检查编译器的行为,收到的结果让我很难理解。
示例 #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 更专业吗?相反,编译器会引发 ambiguous call 错误。
示例 #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& 和 T 是不明确的,那么为什么这里没有一个呼叫是不明确的? X<int> 可以从 X<int&> 构造,X<int&> 可以从 X<int> 构造,这要归功于提供的构造函数。是不是因为编译器生成的X<int>::X(X<int> const&) copy-constructor 是一个比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 完成所有测试,没有额外的标志。
【问题讨论】:
-
你为什么用
template <class T>而不是template <typename T>?第二个版本我从来没有遇到过问题 -
@msrd0 这没什么区别。
class和typename在这种情况下是同一个意思。 -
当您将函数转换为普通的非模板函数时,您的前两个示例同样模棱两可且明确(分别)。我感觉你让事情变得不必要地复杂了。
-
@hvd 偏序使得模板在重载解析中的行为与常规函数略有不同,这就是问题所在,为什么有时这些模板是有序的,为什么在其他情况下它们不是跨度>
标签: c++ templates language-lawyer overload-resolution partial-ordering