這個問題在這里已經有了答案: 使用 std::enable_if 作為模板時的默認模板引數。引數:為什么兩個模板函式只在 enable_if 引數上不同? (2 個回答) 9 天前關閉。
我有以下代碼:
#include <iostream>
/*
template <class A, std::enable_if_t<!std::is_same_v<A, double>, bool> = true>
void test() {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
template <class A, std::enable_if_t<std::is_same_v<A, double>, bool> = true>
void test() {
std::cout << "SFINAE" << std::endl;
}
*/
template <class A, typename = std::enable_if_t<!std::is_same_v<A, double>>>
void test() {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
template <class A, typename = std::enable_if_t<std::is_same_v<A, double>>>
void test() {
std::cout << "SFINAE" << std::endl;
}
int main() {
test<int>();
test<double>();
}
編譯器抱怨
test_function_templ.cpp:21:6: error: redefinition of ‘template<class A, class> void test()’
21 | void test() {
| ^~~~
test_function_templ.cpp:16:6: note: ‘template<class A, class> void test()’ previously declared here
16 | void test() {
| ^~~~
test_function_templ.cpp: In function ‘int main()’:
test_function_templ.cpp:27:15: error: no matching function for call to ‘test<double>()’
27 | test<double>();
| ^
test_function_templ.cpp:16:6: note: candidate: ‘template<class A, class> void test()’
16 | void test() {
| ^~~~
test_function_templ.cpp:16:6: note: template argument deduction/substitution failed:
In file included from /opt/rh/devtoolset-10/root/usr/include/c /10/bits/move.h:57,
from /opt/rh/devtoolset-10/root/usr/include/c /10/bits/nested_exception.h:40,
from /opt/rh/devtoolset-10/root/usr/include/c /10/exception:148,
from /opt/rh/devtoolset-10/root/usr/include/c /10/ios:39,
from /opt/rh/devtoolset-10/root/usr/include/c /10/ostream:38,
from /opt/rh/devtoolset-10/root/usr/include/c /10/iostream:39,
from test_function_templ.cpp:1:
/opt/rh/devtoolset-10/root/usr/include/c /10/type_traits: In substitution of ‘template<bool _Cond, class _Tp> using enable_if_t = typename std::enable_if::type [with bool _Cond = false; _Tp = void]’:
test_function_templ.cpp:15:20: required from here
/opt/rh/devtoolset-10/root/usr/include/c /10/type_traits:2554:11: error: no type named ‘type’ in ‘struct std::enable_if<false, void>’
2554 | using enable_if_t = typename enable_if<_Cond, _Tp>::type;
| ^~~~~~~~~~~
如果我使用第一組函式模板(在代碼中注釋)test(),它會按預期編譯和運行。
問題
我想到了第二組函式模板,呼叫
test<int>()會實體化test<int, void>(),呼叫test<double>()會實體化test<double, void>()。但是編譯器好像看到了兩個重復的模板函式?為什么第一組函式模板沒有任何問題而第二組有問題?
uj5u.com熱心網友回復:
此處描述了第一個片段的問題(請參閱/* WRONG */vs/* RIGHT */代碼片段如何分別映射到您的注釋和未注釋代碼)。
一個常見的錯誤是宣告兩個僅在默認模板引數上不同的函式模板。這不起作用,因為宣告被視為同一函式模板的重新宣告(默認模板引數在函式模板等價中不考慮)。
這是我對為什么會這樣的理解。
當編譯器看到這個(正確的版本)
template <class A, std::enable_if_t<!std::is_same_v<A, double>, bool> = true>
void test() {}
template <class A, std::enable_if_t<std::is_same_v<A, double>, bool> = true>
void test() {}
它沒有看到重新宣告,因為它不知道兩者std::enable_if_t是否會決議為相同的型別。如果它知道,那么它將是一個硬編譯時錯誤,就像這是一個錯誤:
template <class A, bool = true>
void test() {}
template <class A, bool = false> // no matter the value; signature is the same
void test() {}
這并不排除歧義可能發生在替換級別。例如,原則上這些多載宣告沒有問題
template <class A, std::enable_if_t<std::is_convertible_v<A, double>, bool> = true>
void test() {}
template <class A, std::enable_if_t<std::is_convertible_v<A, int>, bool> = true>
void test() {}
但是一旦您呼叫test<int>(),歧義就會出現,因為編譯器將能夠“成功”實體化每個多載,這兩者都會導致template<int, bool = whatever>,這使它們變得模棱兩可。
關于錯誤版本:
template <class A, typename = std::enable_if_t<!std::is_same_v<A, double>>>
void test() {}
template <class A, typename = std::enable_if_t<std::is_same_v<A, double>>>
void test() {}
問題是,在看到它是如何使用之前,編譯器已經抱怨了,因為默認模板引數沒有在函式模板等價中考慮。事實上,一個更簡單的例子顯示了前面代碼片段的問題:
template <class A, typename = typename A::foo>
void test() {}
template <class A, typename = typename A::bar>
void test() {}
請注意,與前面的代碼片段完全一樣,在最后一個代碼片段中,每個多載本身都是正確的,直到您嘗試將其與A默認引數的運算式沒有意義的特定物件一起使用,從而導致硬錯誤.
但是,如果將兩個多載放在一起,則會產生歧義,因為函式模板等價中沒有考慮默認模板引數。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/365031.html
