我正在尝试实现一个模板类,如果模板参数正确的话,它既可以用作派生的基础,也可以用作具体的类。
我想要实现的是,如果模板类的方法无法实例化,但派生类提供了实现,那就没问题。 但是,如果模板可以完全实例化,那么该类对它自己有效。
示例:
// interface class
class A
{
public:
virtual void foo() = 0;
virtual ~A() {}
};
// template class
template <typename T>
class B : public A
{
public:
/* if this tenplate can be instantiated */
foo()
{
T obj;
std::cout << obj;
}
/* else
foo() = 0;
*/
};
// concrete classes
// will work on it's own
typedef B<std::string> C;
class D : public B<void>
{
// B<void>::foo won't instantiate on it's own
// so we provide help here
foo() {}
};
int main(int argc, char const *argv[])
{
A * = new C(); // all good
A * = new D(); // error: cannot instantiate B<void>::foo
return 0;
}
有没有办法达到这样的效果?
答案 0 :(得分:3)
使用SFINAE,您可能会执行以下操作:
namespace detail
{
// an helper for traits
template <typename T>
decltype(T{}, std::cout << T{}, std::true_type{})
helper_has_default_constructor_and_foo(int);
template <typename T>
std::false_type helper_has_default_constructor_and_foo(...);
// the traits
template <typename T>
using has_default_constructor_and_foo = decltype(helper_has_default_constructor_and_foo<T>(0));
// genaral case (so when traits is false)
template <typename T, typename = has_default_constructor_and_foo<T>>
struct C : public A {};
// specialization when traits is true
template <typename T>
struct C<T, std::true_type> : public A
{
void foo() override { std::cout << T{}; }
};
}
最后:
template <typename T>
class B : public detail::C<T>
{
};
答案 1 :(得分:0)
您可以专注于B<void>
:
// template class
template <typename T>
class B : public A
{
public:
virtual void foo()
{
T obj;
std::cout << obj;
}
};
template <>
class B<void> : public A
{
public:
virtual void foo() = 0;
};
答案 2 :(得分:0)
您必须专门化B<>
并且不能使用SFINAE(在成员foo
上)。 SFINAE仅适用于模板,但成员函数模板不能是virtual
。
有不同的方法可以实现专业化,但最直接的是简单明了的
template<typename T>
class B : public A
{
/* ... */ // not overridden foo() by default
};
template<>
class B<WhatEver> : public A
{
virtual foo();
};