基于CRTP的解决方案看起来像什么?

时间:2015-12-11 12:25:48

标签: c++ c++11 crtp

我在this帖子中提出了以下问题(为方便起见,粘贴在下面)。其中一条评论表明存在基于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://stackoverflow.com/a/11744832/2725810 
};

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

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

2 个答案:

答案 0 :(得分:1)

要知道一个类是否来自一个基类,我们有std :: is_base_of&lt;&gt;模板结构,可以与部分特化一起使用,或者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

答案 1 :(得分:0)

这是我自己的第一个解决方案。它不是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://stackoverflow.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成员三次!就我的目的而言,这个缺点超过了这种方法提供的反射的好处。那么,是否有人建议采用更好的方法?