我有这样的代码:
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;}
,但事情没有按我预期的那样进行。编译器错误:
”非类型模板参数不能具有类型'typename std :: enable_if :: value,Bar> :: type'(aka 'Bar')“,
此代码有什么问题?
答案 0 :(得分:7)
您应该同时使print()
重载到函数模板(以使SFINAE工作),否则总是首选非模板函数。
您应该让print()
使用自己的模板类型参数;类型检查不应直接在类模板参数T
上执行,功能模板重载解析和SFINAE不在功能模板本身上执行,而类模板则不涉及。
您可以将std::enable_if
的一部分移到返回类型。
如果您希望类型为std::is_base_of<Bar, X>
或的派生类,则应将指定的顺序更改为std::is_base_of
(即std::is_base_of<X, Bar>
,而不是Bar
)。 Bar
。
例如
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;
}
答案 1 :(得分:2)
由于您实际上对某个类型具有成员函数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;
};
答案 2 :(得分:1)
您应该在调用h1{
text-align:center;
}
时添加模板参数,因为它本身是模板方法。无论如何,您的设计都过于复杂!
使用C ++ 17变得非常简单,您只需一个 print()
方法即可。
Foo<T>::print()
Demo。