C++ 基于CRTP的解决方案会是什么样子?

C++ 基于CRTP的解决方案会是什么样子?,c++,c++11,crtp,C++,C++11,Crtp,我在帖子里问了以下问题(为了方便贴在下面)。其中一条评论建议,有一种基于CRTP的解决方案。我无法理解CRTP在这里的相关性(我以前从未使用过CRTP,所以我不习惯用这些术语来思考)。那么,基于CRTP的解决方案会是什么样子呢 以下是引述的问题: 是否可以编写一个模板函数,该函数将拥有关于模板参数基类的类型信息?(假设模板参数仅从一个类派生) 所以,我在寻找这样的东西: template <class T> auto f(T t) -> decltype(...) { //

我在帖子里问了以下问题(为了方便贴在下面)。其中一条评论建议,有一种基于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;
}  

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

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

下面演示如何使用部分专用结构应用操作,具体取决于该操作是否派生自node_base(在本例中,它仅打印基本对象,但您可以执行任何其他操作)


这是我自己的第一个解决方案。但它不是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;
}
模板
结构管理节点;
//对于不派生的类
模板结构ManagedNode{
使用Base1=void;使用Base2=void;使用Base3=void;
使用Base4=void;
};
//以避免无法接近的基础
//看http://stackoverflow.com/q/34255802/2725810
结构Inter0:public ManagedNode{};
//对于从单个基类派生的类
模板
结构ManagedNode:public Inter0,
公共基地1_{
使用Base1=Base1;
};
//以避免无法接近的基础
模板
结构Inter1:公共管理的节点{};
//对于从两个基类派生的类
模板
结构ManagedNode:public Inter1,
公共基地2_{
使用Base2=Base2;
};
//一些用于测试概念的用户类
结构A:公共ManagedNode{
int data1;
};
结构B:公共管理的节点{};
结构C:公共管理节点{};
int main(){
C C;

std::您是否可以假设您可以将功能的客户端类约束为使用特殊语法来声明其成员变量。但是您似乎不愿意将它们约束为使用特殊语法来声明其基类。我不知道您需求的所有细节,但CRTP似乎可以d是强制客户端类声明其基类的特殊语法的一部分。
struct NodeData:public baseHolder
这假设我知道可能的基类列表,但事实并非如此——整个类层次结构来自user.Hmm。如果此算法的设计需要反射,则可能存在更优雅的方式来表达解决方案。有一个很好的理由,反射被排除在标准之外——这是不必要的。
#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
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;
}