C++ CRTP子类和实例列表

C++ CRTP子类和实例列表,c++,templates,namespaces,static-members,crtp,C++,Templates,Namespaces,Static Members,Crtp,我正在尝试用多重继承实现一种CRTP(如果我很了解它是什么的话) 我的主要目标是有一个统一的方法来访问每个子类的实例列表 可能问题似乎存在于命名空间利用率中 以下是最简单版本的代码: 我真正的问题更类似于: 我使用clang++还有一个额外的警告: test.cpp:28:63: warning: static data member specialization of 'instances' must originally be declared in namespace 'NS1'; a

我正在尝试用多重继承实现一种CRTP(如果我很了解它是什么的话)

我的主要目标是有一个统一的方法来访问每个子类的实例列表

可能问题似乎存在于命名空间利用率中

以下是最简单版本的代码:

我真正的问题更类似于:

我使用clang++还有一个额外的警告:

test.cpp:28:63: warning: static data member specialization of 'instances' must originally be declared in namespace 'NS1'; accepted as a C++0x extension [-Wc++0x-extensions]
template <> std::list<NS1::Derived*> NS1::Base<NS1::Derived>::instances;
                                                          ^ 
test.cpp:15:34: note: explicitly specialized declaration is here
        static std::list<T*> instances;
test.cpp:28:63:警告:“实例”的静态数据成员专门化最初必须在命名空间“NS1”中声明;接受为C++0x扩展[-Wc++0x扩展]
模板std::list NS1::Base::instances;
^ 
test.cpp:15:34:注意:这里是显式专门化声明
静态std::列表实例;
问题已更新,因为它在使用名称空间时的行为不同

将构造函数中的
Base()
intermediate()
更改为
Base()
intermediate
以在Ideone上发布代码时重新编辑的问题

在第二种情况下,没有理由更改
实例的定义:模板与第一种情况相同。

将构造函数中的
Base()
mediator()
更改为
Base()
,而
mediator
生成GCC的代码


在第二种情况下,没有理由更改
实例的定义:模板与第一种情况相同。

好的,您有以下选项。
首先,如果
Intermediate
始终在派生类型上模板化,则不需要为其创建列表,因为它永远不会是最派生的类型。如果可以在其他类型上模板化/不能派生,则可以添加默认的非类型bool模板参数,如下所示:

template<bool, class A, class B>
struct select_base{
  typedef A type;
};

template<class A, class B>
struct select_base<false,A,B>{
  typedef B type;
};

template<class T, bool IsDerived = false>
class Intermediate
  : public select_base<IsDerived,
                       Base<T>,
                       Base<Intermediate<T> >
                       >::type
{
  // ...
};

// derived use
class Derived : public Intermediate<Derived, true>
{
  // ...
};

// non-derived use:
Intermediate<int> im;
当中间层也从
Base
派生,但没有模板化时,就会出现大问题。您可以添加默认的派生类型,但这会使非派生类型的使用更加难看:

#include <type_traits> // C++0x, use std::
//#include <tr1/type_traits> // C++03, use std::tr1::

struct nil_base{};

template<class Derived = nil_base>
class Intermediate
  : public select_base<std::is_same<Derived,nil_base>::value,
                       Base<Intermediate<Derived> >, //
                       Base<Derived>
                       >::type
{
  // ...
};

// derived use now without boolean flag
class Derived : public Intermediate<Derived>
{
  // ...
};

// non-derived use a bit uglier
Intermediate<> im;
//          ^^ -- sadly needed
#包括//C++0x,使用std::
//#包括//C++03,使用std::tr1::
结构nil_base{};
模板
中级班
:public select_base::type
{
// ...
};
//不带布尔标志的派生用法
派生类:公共中间类
{
// ...
};
//非衍生用途有点难看
中间im;
//^^--非常需要

好的,您有以下选项。
首先,如果
Intermediate
始终在派生类型上模板化,则不需要为其创建列表,因为它永远不会是最派生的类型。如果可以在其他类型上模板化/不能派生,则可以添加默认的非类型bool模板参数,如下所示:

template<bool, class A, class B>
struct select_base{
  typedef A type;
};

template<class A, class B>
struct select_base<false,A,B>{
  typedef B type;
};

template<class T, bool IsDerived = false>
class Intermediate
  : public select_base<IsDerived,
                       Base<T>,
                       Base<Intermediate<T> >
                       >::type
{
  // ...
};

// derived use
class Derived : public Intermediate<Derived, true>
{
  // ...
};

// non-derived use:
Intermediate<int> im;
当中间层也从
Base
派生,但没有模板化时,就会出现大问题。您可以添加默认的派生类型,但这会使非派生类型的使用更加难看:

#include <type_traits> // C++0x, use std::
//#include <tr1/type_traits> // C++03, use std::tr1::

struct nil_base{};

template<class Derived = nil_base>
class Intermediate
  : public select_base<std::is_same<Derived,nil_base>::value,
                       Base<Intermediate<Derived> >, //
                       Base<Derived>
                       >::type
{
  // ...
};

// derived use now without boolean flag
class Derived : public Intermediate<Derived>
{
  // ...
};

// non-derived use a bit uglier
Intermediate<> im;
//          ^^ -- sadly needed
#包括//C++0x,使用std::
//#包括//C++03,使用std::tr1::
结构nil_base{};
模板
中级班
:public select_base::type
{
// ...
};
//不带布尔标志的派生用法
派生类:公共中间类
{
// ...
};
//非衍生用途有点难看
中间im;
//^^--非常需要

以下是使用MinGW g++4.4.1、MSVC 10.0和Comeau Online 4.3.10.1编译的OK:

#include <list>

template <class T>
class Base
{
protected:
    Base()
    {
        instances.push_back(static_cast<T*>(this));
    }
private:
    static std::list<T*> instances;
};

template <class U>
class Intermediary : public Base<U>
{
protected:
    Intermediary()
    :Base<U>()
    {
    }
};

class Derived : public Intermediary<Derived>
{
public:
    Derived()
    :Intermediary<Derived>()
    {
    }
};

template<class Derived> std::list<Derived*> Base<Derived>::instances;

int main()
{}
#包括
模板
阶级基础
{
受保护的:
Base()
{
push_back(静态_cast(this));
}
私人:
静态std::列表实例;
};
模板
类别中介:公共基础
{
受保护的:
中间人()
:Base()
{
}
};
派生类:公共中介
{
公众:
派生的()
:中间人()
{
}
};
模板std::列表基::实例;
int main()
{}
实例
定义是从您的问题中逐字复制的

我说,作为艾萨克·牛顿,我没有提出任何假设


Cheers&hth.,

以下是使用MinGW g++4.4.1、MSVC 10.0和Comeau Online 4.3.10.1编译的OK:

#include <list>

template <class T>
class Base
{
protected:
    Base()
    {
        instances.push_back(static_cast<T*>(this));
    }
private:
    static std::list<T*> instances;
};

template <class U>
class Intermediary : public Base<U>
{
protected:
    Intermediary()
    :Base<U>()
    {
    }
};

class Derived : public Intermediary<Derived>
{
public:
    Derived()
    :Intermediary<Derived>()
    {
    }
};

template<class Derived> std::list<Derived*> Base<Derived>::instances;

int main()
{}
#包括
模板
阶级基础
{
受保护的:
Base()
{
push_back(静态_cast(this));
}
私人:
静态std::列表实例;
};
模板
类别中介:公共基础
{
受保护的:
中间人()
:Base()
{
}
};
派生类:公共中介
{
公众:
派生的()
:中间人()
{
}
};
模板std::列表基::实例;
int main()
{}
实例
定义是从您的问题中逐字复制的

我说,作为艾萨克·牛顿,我没有提出任何假设


干杯&hth.,

问题在于您试图错误地定义列表变量。一般来说,您需要为Base提供一个定义——您不能只为恰好是派生的子类的一个部分定义它,除非它是一个显式的专门化

template<typename T> std::list<T*> NS1::Base<T>::instances;
template std::list NS1::Base::instances;


编译时没有错误。没有中间产物或类似的东西是必需的。

问题是您试图错误地定义列表变量。一般来说,您需要为Base提供一个定义——您不能只为恰好是派生的子类的一个部分定义它,除非它是一个显式的专门化

template<typename T> std::list<T*> NS1::Base<T>::instances;
template std::list NS1::Base::instances;


编译时没有错误。没有需要的中间类或类似的东西。

为什么需要重新生成实例列表?我必须为每个最终子类初始化它,因为我希望它是最终子类的列表。注意,在使用中介类的第二个方法中,我没有在中介实现中初始化列表。是链接器问题还是编译器错误?即使你传递了编译器错误,你也可能会因为你的防御而得到链接器问题。这是一个编译器错误,尽管g++在链接之前没有给出任何错误。为什么你需要重新创建实例lis