基于 CRTP 的解决方案会是啥样子?

Posted

技术标签:

【中文标题】基于 CRTP 的解决方案会是啥样子?【英文标题】:What would a CRTP-based solution to this look like?基于 CRTP 的解决方案会是什么样子? 【发布时间】:2015-12-11 12:25:48 【问题描述】:

我在this 帖子中提出了以下问题(为方便起见,粘贴在下面)。其中一位 cmets 建议有一个基于 CRTP 的解决方案来解决这个问题。我无法弄清楚 CRTP 在这里是如何相关的(好吧,我以前从未使用过 CRTP,所以我不习惯用这些术语来思考)。那么,基于 CRTP 的解决方案会是什么样子?

这是引用的问题:

是否可以编写一个模板函数来拥有关于模板参数的基类的类型信息? (假设模板参数只派生自一个类)

所以,我正在寻找这样的东西:

template <class T> 
auto f(T t) -> decltype(...)  // ... is some SFINAE magic that 
                               //     catches B, the base of T
    std::cout << (B)t << std::endl;
  

一些相关背景:我正在编写A* 算法的通用实现。模板参数是一个Node 结构。因此,用户可以定义:

struct NodeBase 
    REFLECTABLE((double)g, (double)f)
        // Using the REFLECTABLE macro as described here:                    
        // https://***.com/a/11744832/2725810 
;

struct NodeData : public NodeBase 
    using Base1 = NodeBase;
    REFLECTABLE((double)F)
;

我想编写一个打印节点结构内容的函数。 REFLECTABLE 完成了提取struct 字段的所有艰苦工作。但是,当用户给我一个NodeData 实例时,我的函数还需要打印NodeBase 组件的内容。我想稍后为两个和三个基类添加我的函数的重载。

【问题讨论】:

您似乎在假设您可以将功能的客户端类限制为使用特殊语法来声明其成员变量。但是,您似乎并不愿意将它们限制为使用特殊语法来声明它们的基类。我不知道您要求的所有细节,但 CRTP 可能是您强制客户端类用来声明其基类的特殊语法的一部分似乎是合理的。 struct NodeData : public baseHolder&lt;NodeData,NodeBase&gt; 【参考方案1】:

要知道一个类是否派生自基类,我们有 std::is_base_of 模板结构,它可以与部分特化或 std::enable_if 结合使用。

这是一个使用部分专用结构来应用操作的演示,具体取决于它是否从 node_base 派生(在这种情况下,它只是打印基础对象,但您可以执行任何其他操作)

#include <iostream>
#include <type_traits>

// base class
struct node_base


;

std::ostream& operator<<(std::ostream& os, const node_base& nb)

    os << "node_base_stuff";
    return os;


// a class derived from node_base
struct node : public node_base


;

// a class not derived from node_base    
struct not_node


;

// apply the general case - do nothing
template<class T, class = void>
struct report_impl

    static void apply(const T&) ;
;

// apply the case where an object T is derived from node_base    
template<class T>
struct report_impl<T, std::enable_if_t< std::is_base_of<node_base, T>::value > >

    static void apply(const T& t) 
        std::cout << static_cast<const node_base&>(t) << std::endl;
    ;
;

// the general form of the report function defers to the partially
// specialised application class
template<class T>
void report(const T& t)

    report_impl<T>::apply(t);


using namespace std;

// a quick test    
auto main() -> int

    node n;
    not_node nn;
    report(n);
    report(nn);

    return 0;

预期输出:

node_base_stuff

【讨论】:

这假设我知道可能的基类列表,但事实并非如此——整个类层次结构来自用户。 嗯。如果这个算法的设计需要反思,那么可能有一种更优雅的方式来表达解决方案。反射被排除在标准之外是有充分理由的 - 这是不必要的。【参考方案2】:

这是我自己的第一个解决方案。虽然它不是 CRTP,但它有一个巨大的缺点,正如答案末尾所解释的那样:

template <class Base1_ = void, class Base2_ = void, class Base3_ = void,
          class Base4_ = void>
struct ManagedNode;

// For classes that do not derive
template <> struct ManagedNode<void, void, void, void> 
    using Base1 = void; using Base2 = void; using Base3 = void;
    using Base4 = void;
;
// To avoid inaccessible base
// See http://***.com/q/34255802/2725810
struct Inter0: public ManagedNode<>;

// For classes that derive from a single base class
template <class Base1_>
struct ManagedNode<Base1_, void, void, void> : public Inter0,
                                               public Base1_ 
    using Base1 = Base1_;
;
// To avoid inaccessible base
template <class Base1_>
struct Inter1: public ManagedNode<Base1_>;

// For classes that derive from two base classes
template <class Base1_, class Base2_>
struct ManagedNode<Base1_, Base2_, void, void> : public Inter1<Base1_>,
                                                 public Base2_ 
    using Base2 = Base2_;
;

// Some user classes for testing the concept

struct A : public ManagedNode<> 
    int data1;
;

struct B : public ManagedNode<> ;

struct C : public ManagedNode<A, B> ;

int main() 
    C c;
    std::cout << sizeof(c) << std::endl;
    return 0;

此代码产生 12 的输出,这意味着 c 包含 data1 成员 3 次!出于我的目的,这个缺点超过了这种方法提供的反射的好处。那么,有人对更好的方法有建议吗?

【讨论】:

以上是关于基于 CRTP 的解决方案会是啥样子?的主要内容,如果未能解决你的问题,请参考以下文章

向 Condor 提交作业时,Scala 提交文件会是啥样子?

需要向包含“保存”按钮的 <p> 添加一些 CSS,选择器会是啥样子? [复制]

Spring Flux:如果我需要使用 Flux 和 Spring 5 进行并行 Web 服务调用,实现模式会是啥样子

如果 Ruby 中的程序创建散列并将这些散列保存在另一个文件中,它会是啥样子?

创新产品的需求分析:未来的图书会是什么样子?

创新产品的需求分析:未来的图书会是什么样子?