如何为特定类型专门化模板 class 方法?
How to specialize a template class method for a specific type?
我有这样的代码:
class Bar {
public:
void print() {
std::cout << "bar\n";
}
};
template<typename T>
class Foo {
public:
template <typename std::enable_if<std::is_base_of<T,Bar>::value,T>::type>
void print() {
t.print();
}
template <typename>
void print() {
std::cout << t << std::endl;
}
private:
T t;
};
int main() {
// Foo<int> foo1;
Foo<Bar> foo2;
foo2.print();
}
这段代码的目的是:如果T t
是Bar
或Bar
的子类,则foo.print()
推导为void print() {t.print();}
,否则推导为 void print() {std::cout << t << std::endl;}
,但事情并没有像我预期的那样工作。编译器错误:
"a non-type template parameter cannot have type 'typename
std::enable_if::value, Bar>::type' (aka
'Bar')",
这段代码有什么问题?
您应该将 print()
重载到函数模板(以使 SFINAE 工作),否则始终首选 non-template 函数。
你应该让print()
有自己的模板类型参数;类型检查不应直接在 class 模板参数 T
上执行,函数模板重载解析和 SFINAE 是在函数模板本身上执行的,class 模板不参与。
您可以将std::enable_if
的部分移动到return类型。
如果您希望类型为 Bar
,则应将指定的顺序更改为 std::is_base_of
(即 std::is_base_of<Bar, X>
,而不是 std::is_base_of<X, Bar>
)或Bar
.
的派生 class
例如
template <typename X = T>
typename std::enable_if<std::is_base_of<Bar, X>::value>::type print() {
t.print();
}
template <typename X = T>
typename std::enable_if<!std::is_base_of<Bar, X>::value>::type print() {
std::cout << t << std::endl;
}
您应该在调用 print()
时添加模板参数,因为它本身就是一个模板方法。反正你的设计太复杂了!
使用 C++17 变得非常简单,您只需要 一个 Foo<T>::print()
方法。
void print() {
if constexpr(std::is_base_of_v<T,Bar>) // ignores `Foo<int>`
t.print();
}
Demo.
由于您实际上对类型是否具有成员函数 print
或定义了 operator<<
感兴趣,因此您也应该以这种方式对其进行约束。
随着即将推出的 C++20
标准,我们得到 concepts & constraints。考虑到这一点,我们可以执行以下操作:
namespace traits
{
template<typename T>
concept has_print_v = requires(T&& t) { t.print(); };
template<typename T>
concept has_ostream_op_v = requires(T&& t, std::ostream& os) { os << t; };
} // end of namespace traits
并使用这样的概念:
template<typename T>
class Foo
{
public:
void print()
{
if constexpr (traits::has_print_v<T>) { t.print(); }
else if constexpr (traits::has_ostream_op_v<T>) { std::cout << t << "\n"; }
}
private:
T t;
};
LIVE DEMO
我有这样的代码:
class Bar {
public:
void print() {
std::cout << "bar\n";
}
};
template<typename T>
class Foo {
public:
template <typename std::enable_if<std::is_base_of<T,Bar>::value,T>::type>
void print() {
t.print();
}
template <typename>
void print() {
std::cout << t << std::endl;
}
private:
T t;
};
int main() {
// Foo<int> foo1;
Foo<Bar> foo2;
foo2.print();
}
这段代码的目的是:如果T t
是Bar
或Bar
的子类,则foo.print()
推导为void print() {t.print();}
,否则推导为 void print() {std::cout << t << std::endl;}
,但事情并没有像我预期的那样工作。编译器错误:
"a non-type template parameter cannot have type 'typename std::enable_if::value, Bar>::type' (aka 'Bar')",
这段代码有什么问题?
您应该将
print()
重载到函数模板(以使 SFINAE 工作),否则始终首选 non-template 函数。你应该让
print()
有自己的模板类型参数;类型检查不应直接在 class 模板参数T
上执行,函数模板重载解析和 SFINAE 是在函数模板本身上执行的,class 模板不参与。您可以将
std::enable_if
的部分移动到return类型。如果您希望类型为
Bar
,则应将指定的顺序更改为std::is_base_of
(即std::is_base_of<Bar, X>
,而不是std::is_base_of<X, Bar>
)或Bar
. 的派生 class
例如
template <typename X = T>
typename std::enable_if<std::is_base_of<Bar, X>::value>::type print() {
t.print();
}
template <typename X = T>
typename std::enable_if<!std::is_base_of<Bar, X>::value>::type print() {
std::cout << t << std::endl;
}
您应该在调用 print()
时添加模板参数,因为它本身就是一个模板方法。反正你的设计太复杂了!
使用 C++17 变得非常简单,您只需要 一个 Foo<T>::print()
方法。
void print() {
if constexpr(std::is_base_of_v<T,Bar>) // ignores `Foo<int>`
t.print();
}
Demo.
由于您实际上对类型是否具有成员函数 print
或定义了 operator<<
感兴趣,因此您也应该以这种方式对其进行约束。
随着即将推出的 C++20
标准,我们得到 concepts & constraints。考虑到这一点,我们可以执行以下操作:
namespace traits
{
template<typename T>
concept has_print_v = requires(T&& t) { t.print(); };
template<typename T>
concept has_ostream_op_v = requires(T&& t, std::ostream& os) { os << t; };
} // end of namespace traits
并使用这样的概念:
template<typename T>
class Foo
{
public:
void print()
{
if constexpr (traits::has_print_v<T>) { t.print(); }
else if constexpr (traits::has_ostream_op_v<T>) { std::cout << t << "\n"; }
}
private:
T t;
};