CRTP:基于派生类内容启用基类中的方法

2024-01-06

有没有办法从 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"; }
};

只是尝试使用Derived::foo inside enable_if导致错误,引用不完整类(派生类)的无效使用。


有没有办法从 CRTP 基类查询派生类的内容,与 SFINAE 一起使用来启用或禁用基类方法?

是的。它遵循一个最小的工作示例:

#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 http://melpon.org/wandbox/permlink/RYGSpwrzxRQnYmIX.

从上面的代码片段中可以看出,基本思想是使用标记分派和重载方法来启用或禁用基类中的方法,并使用派生类中的方法(如果存在)。

简单地尝试在enable_if内部使用Derived::foo会导致错误,引用不完整类(派生类)的无效使用。

那是因为Derived当你尝试使用它时实际上是不完整的。标准说:

在类说明符的结束}处,类被视为完全定义的对象类型(或完整类型)。

在您的情况下,派生类有一个基类模板,并且出于明显的原因,在后者的实例化过程中,前者不是完整的类型。
而且,Derived不是你的 sfinae 表达式中的实际类型并且(让我说)sfinae 不起作用在这种情况下。这就是我在示例中执行以下操作的原因:

template<typename T = D>
auto dispatch(int) -> decltype(std::declval<T>().foo(), void()) {
    static_cast<T*>(this)->foo();
}

当然,decltypesfinae 表达式也以这种方式使用。你可以做类似的事情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 http://melpon.org/wandbox/permlink/tZroWbTpLimF4Krd.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

CRTP:基于派生类内容启用基类中的方法 的相关文章

  • 是否需要销毁运算符删除的形式才能真正销毁对象?

    C 20 添加了破坏形式operator delete区别于std destroying delete t范围 它导致delete表达式在调用之前不再销毁对象operator delete 目的是在显式调用对象的析构函数和释放内存之前 允许
  • 使用 ADAL v3 使用 ClientID 对 Dynamics 365 进行身份验证

    我正在尝试对我们的在线 Dynamics CRM 进行身份验证以使用可用的 API 我能找到的唯一关于执行此操作的官方文档是 https learn microsoft com en us dynamics365 customer enga
  • 在 C 语言中,为什么数组的地址等于它的值?

    在下面的代码中 指针值和指针地址与预期不同 但数组值和地址则不然 怎么会这样 Output my array 0022FF00 my array 0022FF00 pointer to array 0022FF00 pointer to a
  • strlen() 编译时优化

    前几天我发现你可以找到编译时strlen使用这样的东西 template
  • 在 C++ 代码中转换字符串

    我正在学习 C 并开发一个项目来练习 但现在我想在代码中转换一个变量 字符串 就像这样 用户有一个包含 C 代码的文件 但我希望我的程序读取该文件并插入将其写入代码中 如下所示 include
  • Boost ASIO 串行写入十六进制值

    我正在使用 ubuntu 通过串行端口与设备进行通信 所有消息都必须是十六进制值 我已经在 Windows 环境中使用白蚁测试了通信设置 并得到了我期望的响应 但在使用 Boost asio 时我无法得到任何响应 以下是我设置串口的方法 b
  • 在 Mono 中反序列化 JSON 数据

    使用 Monodroid 时 是否有一种简单的方法可以将简单的 JSON 字符串反序列化为 NET 对象 System Json 只提供序列化 不提供反序列化 我尝试过的各种第三方库都会导致 Mono Monodroid 出现问题 谢谢 f
  • 如何在 C# 中将 Json 转换为对象

    我想将 Json 转换为 C 中的对象 这里的 Json 是 值 e920ce0f e3f5 4c6f 8e3d d2fbc51990e4 如何使用 Object 问题看似愚蠢 但其实并不那么愚蠢 我没有简单的 Json 我有 IEnume
  • 混合模型优先和代码优先

    我们使用模型优先方法创建了一个 Web 应用程序 一名新开发人员进入该项目 并使用代码优先方法 使用数据库文件 创建了一个新的自定义模型 这 这是代码第一个数据库上下文 namespace WVITDB DAL public class D
  • Libev,如何将参数传递给相关回调

    我陷入了 libev 中争论的境地 通常 libev 在类似的函数中接收包 接收回调 没关系 但是实际操作中 我们需要派遣一个亲戚 写回调 根据收到的包裹处理具体工作 例如 S RECV MSG pstRecvMsg S RECV MSG
  • Linux 上的 RTLD_LOCAL 和dynamic_cast

    我们有一个由应用程序中的一些共享库构成的插件 我们需要在应用程序运行时更新它 出于性能原因 我们在卸载旧插件之前加载并开始使用新插件 并且只有当所有线程都使用旧插件完成后 我们才卸载它 由于新插件和旧插件的库具有相同的符号 我们dlopen
  • Unity c# 四元数:将 y 轴与 z 轴交换

    我需要旋转一个对象以相对于现实世界进行精确旋转 因此调用Input gyro attitude返回表示设备位置的四元数 另一方面 这迫使我根据这个四元数作为默认旋转来计算每个旋转 将某些对象设置为朝上的简单方法如下 Vector3 up I
  • 让网络摄像头在 OpenCV 中工作

    我正在尝试让我的网络摄像头在 Windows 7 64 位中的 OpenCV 版本 2 2 中捕获视频 但是 我遇到了一些困难 OpenCV 附带的示例二进制文件都无法检测到我的网络摄像头 最近我发现这篇文章表明答案在于重新编译一个文件 o
  • 当Model和ViewModel一模一样的时候怎么办?

    我想知道什么是最佳实践 我被告知要始终创建 ViewModel 并且永远不要使用核心模型类将数据传递到视图 这就说得通了 让我把事情分开 但什么是Model 和ViewModel一模一样 我应该重新创建另一个类还是只是使用它 我觉得我应该重
  • 调用 .ToArray() 时出现 ArgumentException

    我有一个经常被清除的列表 代码完全是这样的 VisitorAgent toPersist List
  • 任何人都可以清楚地告诉如何在不使用像 这样的预定义函数的情况下找到带有小数值或小数值的指数吗? [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 例如 2 0 5 1 414 所以想要 我是 c 的新手 所以请解释简单的逻辑 如果不是复杂的逻辑也足够了 在数学中 从整数取幂到实数
  • Streamwriter 覆盖 txt 文件中的文本

    有没有什么方法可以重新打开流写入器而不创建新的写入对象 因为此时 当调用 WriteOdd 时 streamwriter 正在覆盖在它之前调用的 WriteEven public void WriteEven StreamWriter wr
  • 如果找不到指定的图像文件,显示默认图像的最佳方式?

    我有一个普通的电子商务应用程序 我将 ITEM IMAGE NAME 存储在数据库中 有时经理会拼错图像名称 为了避免 丢失图像 IE 中的红色 X 每次显示产品列表时 我都会检查服务器中是否有与该产品相关的图像 如果该文件不存在 我会将其
  • ContentDialog Windows 10 Mobile XAML - 全屏 - 填充

    我在项目中放置了一个 ContentDialog 用于 Windows 10 上的登录弹出窗口 当我在移动设备上运行此项目时 ContentDialog 未全屏显示 并且该元素周围有最小的填充 在键盘上可见 例如在焦点元素文本框上 键盘和内
  • 如何在 C# 中获取 CMD/控制台编码

    我需要指定正确的代码页来使用 zip 库打包文件 正如我所见 我需要指定控制台编码 在我的例子中为 866 C Users User gt mode Status for device CON Lines 300 Columns 130 K

随机推荐