希望得到一些關于為什么允許/禁止以下內容的詳細解釋。
#include <type_traits>
#include <iostream>
template<typename T>
std::enable_if_t<std::is_arithmetic_v<T>> foo(T v) {
std::cout << "arithmetic version called\n";
}
template<typename T>
std::enable_if_t<!std::is_arithmetic_v<T>> foo(T v) {
std::cout << "non arithmetic version called\n";
}
int main() {
foo(33);
foo("hello");
}
關于上面的代碼示例,為什么這被允許,因為多載決議不會啟動,因為函式僅因回傳型別而異?
根據上述內容,使用 Expression SFINAE,我必須確保消除多載的歧義,如下面的代碼片段所示。
#include <type_traits>
#include <iostream>
template<typename T, std::enable_if_t<std::is_arithmetic_v<T>>* p = nullptr>
void foo(T v) {
std::cout << "arithmetic version called\n";
}
template<typename T, std::enable_if_t<!std::is_arithmetic_v<T>>* p = nullptr>
void foo(T v) {
std::cout << "non arithmetic version called\n";
}
int main() {
foo(33);
foo("hello");
}
所以我想這個問題更多的是它為什么起作用,即語言規則與如何去做。我已經使用這些技術一段時間了,但經常仍然對哪些語言規則能夠實作這一點感到頭疼。
提前致謝。
#include <type_traits>
#include <iostream>
#include <string>
template<typename T>
auto increment_or_self_(T a, int i) -> std::decay_t<decltype( std::declval<T&>(), std::declval<T>())> {
a;
return a;
}
template<typename T>
auto increment_or_self_(T a, ...) -> T {
return a;
}
template<typename T>
T increment_or_self(T a) {
return increment_or_self_(a, 0);
}
int main() {
std::cout << increment_or_self(33) << std::endl;
std::cout << increment_or_self(std::string("hello")) << std::endl;
}
在上面的 increment_or_self 有一個虛擬 arg 以確保多載沒有歧義。
另外,為什么我們在移動 EnableIf 時必須使用指標,如下所示?
uj5u.com熱心網友回復:
您的第一個示例在多載決議方面沒有問題,因為您沒有兩次具有不同回傳型別的相同函式,因為 SFINAE 的東西已經在每個實體化中禁用了兩者之一。即使您的 SFINAE 運算式將導致兩種不同的回傳型別,這也無關緊要,因為兩個實體化函式將具有不同的簽名,因為它們具有不同的輸入引數。
我修改了您的示例,這也將是格式良好的(請參閱此更改的不同回傳型別)
std::enable_if_t<std::is_arithmetic_v<T>, float> foo(T ) {
std::cout << "arithmetic version called\n";
return 1.1;
}
template<typename T>
std::enable_if_t<!std::is_arithmetic_v<T>, int> foo(T ) {
std::cout << "non arithmetic version called\n";
return 1;
}
int main() {
foo(33);
foo("hello");
std::cout << std::is_same_v< float, decltype(foo(33))> << std::endl;
std::cout << std::is_same_v< int, decltype(foo("Hallo"))> << std::endl;
}
您的問題:“另外,為什么我們在移動 EnableIf 時必須使用指標,如下所示?” 很簡單:std::enable_if只有一個引數的 以 a 結尾void。而且您根本無法為 void 賦值。您可以將代碼更改std::enable_if_t<!std::is_arithmetic_v<T>, bool> p = true為現在您的std::enable_if結果在一個 bool 中,您可以在其中分配一個值,例如true.
使用 C 20,您可以通過使用來簡化很多 concepts
template <typename T> concept arithmetic = std::is_arithmetic_v<T>;
template< arithmetic T>
void foo(T) {
std::cout << "arithmetic version called\n";
}
void foo(auto) {
std::cout << "non arithmetic version called\n";
}
int main() {
foo(33);
foo("hello");
}
如您所見,我們不需要定義“非算術”概念,因為我們可以簡單地相信規則,如果能夠實體化更多模板,將使用更指定的模板。
uj5u.com熱心網友回復:
請注意,您沒有指定不同的回傳值,兩個函式都回傳一個 void。所以那里沒有問題。SFINAE 只會確保其中一個函式不會導致可編譯的回傳型別(但不會給出錯誤,因為替換不是錯誤)。
不要專注于 SFINAE,c 不斷改進元模板編程技術,在這種情況下,我將只使用 if constexpr 解決方案。像這樣:
#include <type_traits>
#include <iostream>
template<typename T>
void foo(const T& v) // <== I prefer not to use pass by value for template parameters so I pass by reference (avoid copying)
{
if constexpr (std::is_arithmetic_v<T>)
{
std::cout << "arithmetic version called\n";
}
else
{
std::cout << "non arithmetic version called\n";
}
}
int main()
{
foo(33);
foo("hello");
return 0;
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/402186.html
