我有一个抽象基类,然后是一个模板化派生类,对于某些类型(由模板指定),我想在派生类中实现虚拟基方法,对于其他类型,这应该保持开放状态(需要进一步推导):
#include <iostream>
struct cat {
void meow() {
std::cout << "Meow!\n";
}
};
struct base {
virtual void foo_base() = 0;
};
template<typename T>
concept can_meow = requires(T t){t.meow();};
template<class T>
struct derived: public base {
derived() requires can_meow<T> {
std::cout << "Wrapper constructor with meow!\n";
}
derived() requires (!can_meow<T>) {
std::cout << "Wrapper constructor w/o meow!\n";
}
void foo_base() requires can_meow<T> final {};
};
template<class T>
struct derived2: public derived<T> {
void foo_base() final {};
};
int main() {
derived<cat> c; //works
//derived2<int> i; //error
return 0;
}
如何让它发挥作用?我目前遇到的错误是:
<source>:28:6: error: use of function 'void wrapper<T>::foo_base() requires can_meow<T> [with T = int]' with unsatisfied constraints
干杯和感谢
更新:godbolt https://godbolt.org/z/T4v7Yjq5f
不允许以任何方式约束虚函数(请参阅:[class.virtual]p6)。
您可以通过使用可以重写该函数的条件基类来实现类似的效果。例如:
namespace detail {
template<class T>
struct derived_impl : base {
using base::base;
};
template<can_meow T>
struct derived_impl<T> : base {
using base::base;
void foo_base() final {}
};
}
template<class T>
struct derived: detail::derived_impl<T> {
derived() requires can_meow<T> {
std::cout << "Wrapper constructor with meow!\n";
}
derived() requires (!can_meow<T>) {
std::cout << "Wrapper constructor w/o meow!\n";
}
};
正如 @artyer 已经指出的那样,您不能以这种方式限制虚拟函数。引入条件中间类的技术是一种很好的技术,但是您可以使用一些 CRTP 技巧来减少侵入性。
template<class D, class B, bool route_foo>
struct foo_router:B {
using B::B;
};
template<class D, class B>
struct foo_router<D,B, true>:B {
using B::B;
void foo_base() final {
return static_cast<D*>(this)->foo_base_impl();
}
};
然后我们做:
template<class T>
struct derived: public foo_router<derived<T>, base, can_meow<T>> {
derived() requires can_meow<T> {
std::cout << "Wrapper constructor with meow!\n";
}
derived() requires (!can_meow<T>) {
std::cout << "Wrapper constructor w/o meow!\n";
}
void foo_base_impl() requires can_meow<T> /*final*/ {};
};
template<class T>
struct derived2: public derived<T> {
void foo_base() final {};
};
foo_router
根据 foo_base
有条件地将 foo_base_impl
路由到 can_meow<T>
。在此实现中,它无论如何都存在,但如果 !can_meow<T>
. 则不执行任何操作
实时示例 - 更改仅限于重命名和从
final
中剥离 foo_base_impl
、额外的 router
类以及从 derived
的继承。