【发布时间】:2016-12-07 20:35:42
【问题描述】:
看看这段代码:
#include <vector>
#include <functional>
template<typename RandIt, typename T, typename Pred>
auto search_with(RandIt begin, RandIt end, const T& value, Pred&& pred) noexcept {
//...
return begin;
}
template<typename RandIt, typename T>
auto search_with(RandIt begin, RandIt end, const T& value) noexcept {
return search_with(begin, end, value, std::less<T>{});
}
template<typename Array, typename T, typename Pred>
auto search_with(const Array& array, const T& value, Pred&& pred) noexcept {
return search_with(std::begin(array), std::end(array), value, std::forward<Pred>(pred));
}
int main() {
std::vector<int> v = { 1, 2, 3 };
search_with(v, 10, std::less<int>{}); // ok
search_with(v.begin(), v.end(), 10); // fail!
}
我不明白为什么在第二个search_with 调用中,编译器选择了第三个重载。如果我注释掉第三个重载,那么代码编译得很好。这表明第二个重载在编译时没有被丢弃,它应该是一个有效的重载。
但是,选择了第三个重载,但失败了,因为迭代器没有专门化 std::begin(和 std::end):
main.cpp: In instantiation of 'auto search_with(const Array&, const T&, Pred&&) [with Array = __gnu_cxx::__normal_iterator<int*, std::vector<int> >; T = __gnu_cxx::__normal_iterator<int*, std::vector<int> >; Pred = int]':
main.cpp:23:39: required from here
main.cpp:17:34: error: no matching function for call to 'begin(const __gnu_cxx::__normal_iterator<int*, std::vector<int> >&)'
return search_with(std::begin(array), std::end(array), value, std::forward<Pred>(pred));
~~~~~~~~~~^~~~~~~
我原以为会发生相反的情况:第三个重载因为编译失败而被丢弃,而选择了第二个。
但显然情况并非如此,那么这里发生了什么?为什么选择了错误的重载?为什么第三个重载比第二个更好?
【问题讨论】:
-
SFINAE 应用于函数声明,而不是定义,这就是为什么 enable_if 应用于模板参数或返回值,而不是在正文中粘贴此类代码的更漂亮的选项。仅当声明位无法编译时,该函数才会被丢弃。提供的参数在第三个函数的声明中工作得很好,所以它不会被丢弃。
-
但是,如果您使用尾随返回类型,它似乎可以工作:coliru.stacked-crooked.com/a/5d3d34290d7beb1a
-
@AndyG 啊,那是 SFINAE。 :) 谢谢
-
@Rakete1111 可能他们决定不这样做,因为那时重载解决方案将取决于问题函数的 内容,并且已经有足够的混乱:P
-
函数重载不能依赖函数体。考虑非模板函数重载的情况。函数体可能不在头文件中,并且在要解决重载时根本不可见。
标签: c++ c++14 overload-resolution