問題是什么:
我正在嘗試實作一個具有兩種專業化的類,一種用于整數型別,一種用于所有其他型別。我想到的第一個版本:
#include <type_traits>
template<typename T, typename std::enable_if_t<std::is_integral<T>::value, bool> = true>
class Test
{
};
template<typename T, typename std::enable_if_t<!std::is_integral<T>::value, bool> = true>
class Test
{
};
但是當我嘗試編譯上面的代碼時,GCC 失敗并出現以下錯誤:
<source>:2:84: error: template parameter 'typename std::enable_if<std::is_integral<_Tp>::value, bool>::type <anonymous>'
template<typename T, typename std::enable_if_t<std::is_integral<T>::value, bool> = true>
^~~~
<source>:6:85: note: redeclared here as 'typename std::enable_if<(! std::is_integral<_Tp>::value), bool>::type <anonymous>'
template<typename T, typename std::enable_if_t<!std::is_integral<T>::value, bool> = true>
^~~~
但是,對函式使用類似的技術可以毫無問題地編譯:
#include <type_traits>
template<typename T, typename std::enable_if_t<std::is_integral<T>::value, bool> = true>
void test()
{
}
template<typename T, typename std::enable_if_t<!std::is_integral<T>::value, bool> = true>
void test()
{
}
我想要達到的目標:
- 首先,我想了解為什么帶有函式的版本可以編譯,而帶有類的版本卻沒有。
- 我的第二個目標是實作一個滿足我一開始指定的條件的類。
我試過的:
使用偏特化可以解決問題 2:
#include <type_traits>
template<typename T, bool = std::is_integral<T>::value>
class Test;
template<typename T>
class Test<T, true>
{
};
template<typename T>
class Test<T, false>
{
};
但是這種方法很糟糕,因為它允許使用Test<float, true>并且如果我理解正確(如果我錯了,請糾正我),然后將使用整數型別的專業化,這不是我想要的。
總結:
我將以問題的形式重復我的目標:
- 為什么帶函式的版本可以編譯,帶類的版本不能編譯?
- 如何實作一個滿足我一開始指定的條件的類?
uj5u.com熱心網友回復:
不能保證在類的模板引數串列中使用std::enable_if,因為該標準僅對函式有 SFINAE,對型別沒有。對于型別,它可能可用,但僅作為非標準編譯器擴展。
C 20 將概念添加到標準中,這也適用于模板類上的模板引數。
template<class T>
concept NotIntegral = !std::integral<T>;
template<class T>
class Test;
template<std::integral T>
class Test<T>
{
public:
void operator()() const
{
std::cout << "integral\n";
}
};
template<NotIntegral T>
class Test<T>
{
public:
void operator()() const
{
std::cout << "not integral\n";
}
};
int main()
{
std::cout << "float: ";
(Test<float>{})();
std::cout << "int: ";
(Test<int>{})();
}
對于 C 的早期版本,您可以將 a 添加static_assert到類主體中,這不會阻止用戶為第二個模板引數指定無效值,但至少編譯代碼會產生您選擇的編譯器錯誤:
template<typename T>
class Test<T, true>
{
static_assert(std::is_integral_v<T>,
"The second template parameter must contain true if and only if the first template parameter is an integral type");
};
您可以引入一個用于創建物件的函式,而不是允許用戶創建具有無效模板引數的物件:
template<typename T, bool = std::is_integral<T>::value>
class Test;
template<class T>
Test<T, std::is_integral<T>::value> MakeTest()
{
return {};
}
template<typename T>
class Test<T, true>
{
friend Test<T, std::is_integral<T>::value> MakeTest<T>();
Test() = default;
public:
void operator()() const
{
std::cout << "integral\n";
}
};
template<typename T>
class Test<T, false>
{
friend Test<T, std::is_integral<T>::value> MakeTest<T>();
Test() = default;
public:
void operator()() const
{
std::cout << "not integral\n";
}
};
int main()
{
std::cout << "float: ";
MakeTest<float>()();
std::cout << "int: ";
MakeTest<int>()();
// Test<int, false> test; // would yield a compiler error because of the inaccessible default constructor
}
uj5u.com熱心網友回復:
沒有概念,您可以以更“私人”的方式進行部分專業化,如下所示:
namespace detail {
template<typename T, bool>
class Test;
template<typename T>
class Test<T, true>
{
};
template<typename T>
class Test<T, false>
{
};
} // namespace detail
template<typename T>
using Test = detail::Test<T, std::is_integral<T>::value>;
旁注:您不需要兩個部分特化,您可以將主定義用于其中一個值true或false(不指定它),并且您可能希望將主Test類重命名為其他名稱,也許TestImpl; 對用戶來說并不重要。
我想提供另一種選擇。我認為重要的是要記住,如果您的兩個專業化非常相似,您可以只使用if constexpr(C 17) 或標記調度而不是部分專業化:
#include <type_traits>
#include <iostream>
template<typename T>
class Test
{
public:
void operator()() const {
if constexpr(std::is_integral<T>::value) {
std::cout << "integral\n";
} else {
std::cout << "not integral\n";
}
}
};
int main()
{
Test<float>{}();
Test<int>{}();
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/527332.html
標籤:C 模板
上一篇:如何在C 17中實作概念
下一篇:在RecycleView中的onLongClickListener()之后的AndroidJavastartActivity
