- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
有没有办法从 CRTP 基类查询派生类的内容,与 SFINAE 一起使用来启用或禁用基类方法?
我想要完成的可能如下所示:
template<typename Derived>
struct base
{
struct foo {};
struct bar {};
void dispatch(int i)
{
switch (i) {
case 0: dispatch(foo{}); break;
case 1: dispatch(bar{}); break;
default: break;
}
}
// catch all for disabled methods
template<typename T> void dispatch(T const&) {}
std::enable_if</* magic that checks if there is in fact Derived::foo(foo) */>
dispatch(foo f)
{
static_cast<Derived*>(this)->foo(f);
}
std::enable_if</* magic that checks if there is in fact Derived::bar(bar) */>
dispatch(bar b)
{
static_cast<Derived*>(this)->bar(b);
}
};
struct derived: public base<derived>
{
// only foo in this one
void foo(foo) { std::cout << "foo()\n"; }
};
简单地尝试在 enable_if
中使用 Derived::foo
会导致错误,引用不完整类(派生类)的无效使用。
最佳答案
Is there a way to query derived class' contents from a CRTP base class, to use with SFINAE to enable or disable base class methods?
是的,是的。它遵循一个最小的工作示例:
#include<iostream>
template<typename D>
class base {
template<typename T = D>
auto dispatch(int) -> decltype(std::declval<T>().foo(), void()) {
static_cast<T*>(this)->foo();
}
void dispatch(char) {
std::cout << "base" << std::endl;
}
public:
void dispatch() {
dispatch(0);
}
};
struct derived1: base<derived1> {
void foo() {
std::cout << "derived1" << std::endl;
}
};
struct derived2: base<derived2> {};
int main() {
derived1 d1;
derived2 d2;
d1.dispatch();
d2.dispatch();
}
添加要转发的参数很简单,我希望示例尽可能简单。
查看它在 wandbox 上运行.
从上面的代码片段可以看出,基本思想是使用标记分派(dispatch)和重载方法来启用或禁用基类中的方法,如果派生类中存在则使用派生类中的方法。
Simply trying to use Derived::foo inside enable_if results in an error citing invalid use of an incomplete class (derived).
那是因为当您尝试使用 Derived
时,它实际上是不完整的。标准说:
A class is considered a completely-defined object type (or complete type) at the closing } of the class-specifier.
在您的例子中,派生类有一个基类模板,并且由于显而易见的原因,在后者的实例化期间前者不是完整类型。
此外,Derived
不是您的 sfinae 表达式中的实际类型,并且(让我说)sfinae 在这种情况下不起作用。这就是我在示例中执行以下操作的原因:
template<typename T = D>
auto dispatch(int) -> decltype(std::declval<T>().foo(), void()) {
static_cast<T*>(this)->foo();
}
当然,这样使用的decltype
也是一个sfinae表达式。如果您愿意,可以使用 std::enable_if_t
做类似的事情。我发现这个版本更易于阅读和理解。
也就是说,您可以通过虚拟方法获得相同的结果。如果您没有充分的理由不这样做,请使用它。
为了完整起见,您的示例已更新为上述技术:
#include<iostream>
template<typename Derived>
struct base
{
struct foo {};
struct bar {};
void dispatch(int i)
{
switch (i) {
case 0: dispatch(0, foo{}); break;
case 1: dispatch(0, bar{}); break;
default: break;
}
}
template<typename T>
void dispatch(char, T const&) {}
template<typename D = Derived>
auto dispatch(int, foo f)
-> decltype(std::declval<D>().foo(f), void())
{
static_cast<D*>(this)->foo(f);
}
template<typename D = Derived>
auto dispatch(int, bar b)
-> decltype(std::declval<D>().bar(b), void())
{
static_cast<D*>(this)->bar(b);
}
};
struct derived: public base<derived>
{
void foo(foo) { std::cout << "foo" << std::endl; }
};
int main() {
derived d;
d.dispatch(0);
d.dispatch(1);
}
查看 wandbox .
关于c++ - CRTP:根据派生类内容启用基类中的方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42514100/
我目前正在实现一个通用事件类。事件处理程序有一个发送者参数和可变数量的事件参数。所以事件类的声明如下: template class event; 为了允许某些实现细节,我需要事件的 CRTP,如下所
我需要实现对实现相同接口(interface)的对象 vector 的高效访问。直到现在,我一直在使用带有虚函数的继承:接口(interface)被定义为具有纯虚函数的抽象类,每个对象类都实现了虚函数
在CRTP ,基类可以使用派生类的函数和变量。但是,派生类的类型不能直接被基类使用,见下面代码: #include template class A { public: //using S
我创建了一个模板类,只要发生实例化,它就会触发运行时文本输出: template struct verbose { verbose() { std::cout str
假设我有一个用于矩阵的 CRTP 模板类 template class MatrixBase{ private: //... public: Derived some_function
我已经阅读了很多关于 Curiously Recurring Template Pattern 的帖子而且我仍然不明白为什么我不想只使用模板编程来使用它。 下面是一个从维基百科稍作修改的例子: tem
请帮我解决以下问题: 我有一个类声明为: template class Rim : /* Derive from GenericComponent Design perspective u
有没有办法从 CRTP 基类查询派生类的内容,与 SFINAE 一起使用来启用或禁用基类方法? 我想要完成的可能如下所示: template struct base { struct foo
我有兴趣了解 CRTP。我想为引擎实现一个组件系统,我不想访问组件统一风格 GetComponent("withThisName"); 而是在编译时(虚幻风格) GetComponent(); 虽然实
让我们考虑一个用于打印派生类的 CRTP 模板类 Print: template struct Print { auto print() const -> void; auto se
最近我一直在摆弄模板并偶然发现了以下问题。我正在像这样实现 CRTP 模式: template struct protocol_object { ... }; struct data_obje
我正在尝试在固定大小的缓冲区中构建一条消息,我的图书馆的用户会在其中提供一些数据。我曾经通过给用户一个指向缓冲区的指针并让他们写入它,并通过引用他们写入的字节数来设置一个 size_t 参数来做到这一
我刚刚遇到 CRTP 的问题,我不能在基类和派生类中使用相同的方法名称(具有不同的签名)。重现此问题的示例如下: template struct Base { void foo(){}
考虑以下代码: #include #include #include struct BaseClass { static int identifier() { stati
我有一个纯虚类接口(interface): class Interface { public: virtual ~Interface() noexcept; virtual voi
任何人都可以向我解释为什么 base::blah(string str) 的签名必须是一个字符串而不是引用一个字符串。如果它是一个字符串,编译器会出现以下错误。是因为在模板实例化时编译器拒绝任何隐式转
我遇到了以下代码(存储在 crtp.cc 中)的编译器相关问题: #include #include #include template class AlgebraicVectorExpres
在我的一个项目中,我使用与此处的答案 1 相同的 CRTP 方法(源自 enable_crtp):How do I pass template parameters to a CRTP? 但是我也需要
我目前正在使用 C++ 模板处理 CRTP 模式。在摆弄 visual studio 时,我发现了几种派生类可以调用函数的基类实现的方式/方法。下面是我正在使用的代码以及 3 行注释掉的代码,显示了如
我想创建一个模板类,为类提供通用方法,使其具有成员 m_Type,指定继承类提供的某种类型。考虑一下: template struct TypeAttribute { T m_Type; };
我是一名优秀的程序员,十分优秀!