的這段代碼看看:編譯器錯誤的選擇,而不是過載超載有效
#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應用於模板參數或返回值,而不是在主體中粘貼這樣的代碼的漂亮選項。只有在聲明位編譯失敗的情況下,該函數纔會被丟棄。提供的參數在第三個函數的聲明中工作得很好,所以它不會被丟棄。 – jaggedSpire
但是,如果您使用尾隨返回類型,它似乎工作:http://coliru.stacked-crooked.com/a/5d3d34290d7beb1a – AndyG
@AndyG啊,那就是SFINAE。 :)謝謝 – Rakete1111