【问题标题】:Eliminate redundancy with CRTP and multiple inheritance通过 CRTP 和多重继承消除冗余
【发布时间】:2026-02-04 14:10:01
【问题描述】:

这个问题是针对 C++03,而不是 C++11。

我有一个案例,我正在使用具有多重继承的 CRTP,我很想知道是否有办法消除在下面指定 B 的类型时创建的冗余。

#include "boost/typeof/typeof.hpp"
#include "boost/units/detail/utility.hpp"
#include <iostream>
#include <string>

struct One{};
struct Two{};

template<typename T>
struct Type
{
   static std::string name(void)
   {
      return boost::units::detail::demangle(typeid(T).name());
   }
};

template<typename T1,
         typename T2>
struct A
{
   typedef A<T1, T2> Self;

   A()
   {
      std::cout << Type<Self>::name() << std::endl;
   }
};

template<typename T1,
         typename T2,
         typename T3>
struct B : public A<One, B<T1, T2, T3> >, // The B<T1, T2, T3> here is redundant
           public A<Two, B<T1, T2, T3> >
{
   typedef B<T1, T2, T3> Self;

   B()
   {
      std::cout << Type<Self>::name() << std::endl;
   }
};

int main(int argc, char* argv[])
{
   B<int, int, int> t;
   return 0;
}

Coliru上看到这个

B 的模板参数数量增加、模板参数本身很复杂以及BA 继承更多次时,问题会更加严重。我想尽量减少B 的模板参数的重复。具体来说,我正在寻找一种方法来访问B 的继承列表中的typedef B&lt;T1, T2, T3&gt; Self,或this 的一些等效编译时版本。

我不能:

  • 使用前向声明为B 上方的B 创建一个typedef,因为我无权访问模板参数
  • 在继承定义中中创建一个typedef,因为语法不允许这样做
  • 从类内部访问 typedef,因为它还不存在

类似下面的东西(都不是无效代码,但显示我正在寻找的效果):

template<typename T1,
         typename T2,
         typename T3>
struct B : public A<One, Self>, // Cannot access the typedef yet
           public A<Two, Self>
{
   typedef B<T1, T2, T3> Self;
};

template<typename T1,
         typename T2,
         typename T3>
struct B : typedef B<T1, T2, T3> Self, // Invalid syntax
           public A<One, Self>, 
           public A<Two, Self>
{

};

template<typename T1,
         typename T2,
         typename T3>
struct B : public A<One, B>, // I wish this would work
           public A<Two, B>
{

};

template<typename T1,
         typename T2,
         typename T3>
struct B : public A<One, BOOST_TYPEOF(*this)>, // lol
           public A<Two, BOOST_TYPEOF(*this)>
{

};

有没有办法访问this 的编译时版本?

【问题讨论】:

  • 使用嵌套类型/元函数怎么样? template&lt;typename T1, typename T2, typename T3&gt; struct M { struct B : A&lt;One, B&gt;, A&lt;Two, B&gt; {}; }; 由于缺少别名模板,在 C++03 中使用当然不如 C++03 好,但可以从中派生:template&lt;typename T1, typename T2, typename T2&gt; struct C : typename M&lt;T1, T2, T3&gt;::B {};

标签: c++ multiple-inheritance c++03 crtp


【解决方案1】:

问题:

template<typename T1,
         typename T2,
         typename T3>
struct B : public A<One, B>, // I wish this would work
           public A<Two, B>
{

};

是您的template &lt;typename T1, typename T2&gt; struct A 要求 用 T2 一个 type 实例化,而你希望你能做的 是用T2一个模板来实例化它,即 template&lt;typename, typename,typename&gt; struct B.

如果A 的定义在您自己的控制之下,那么也许——虽然也许不是——一个解决方案 就是让A的定义和你的愿望一致:

#include "boost/typeof/typeof.hpp"
#include "boost/units/detail/utility.hpp"
#include <iostream>
#include <string>

struct One{};
struct Two{};

template<typename T>
struct Type
{
   static std::string name(void)
   {
      return boost::units::detail::demangle(typeid(T).name());
   }
};

template<typename T1,
         template<typename, typename, typename> class T2
>
struct A
{

   A()
   {
      std::cout << Type<A>::name() << std::endl;
   }
};


template<typename T1,
         typename T2,
         typename T3>
struct B : public A<One, B >,
           public A<Two, B >
{
   B()
   {
      std::cout << Type<B>::name() << std::endl;
   }
};


int main(int argc, char* argv[])
{
   B<int, int, int> t;
   return 0;
}

这个程序打印:

A<One, B>
A<Two, B>
B<int, int, int>

此解决方案的价格限制了 A 可以为实例化模板的那些提供 CRTP 基础,例如 B,正好是三个typename 参数。

也许你很幸运,这个限制并没有阻碍任何其他 希望你有。但如果您还需要A 来提供 CRTP 基础 对于实例化一些不完全是三个模板的类 typename 参数,然后它会咬人。

前提是您需要 A 提供 CRTP 的所有课程 base 是只有 typename 参数的模板的实例化, 并且最多有N,那么你仍然可以在 相同的精神:

您根据架构定义A

template<typename T1,
         template<typename /*1*/,.... typename /*N*/> class T2
>
struct A { ... };

对于每个模板YA 将作为 CRTP 基础,您提供 完全是N 参数,使用默认为的“填充”参数 void,根据需要。例如,如果N == 3:

#include "boost/typeof/typeof.hpp"
#include "boost/units/detail/utility.hpp"
#include <iostream>
#include <string>

struct One{};
struct Two{};

template<typename T>
struct Type
{
   static std::string name(void)
   {
      return boost::units::detail::demangle(typeid(T).name());
   }
};

template<typename T1,
         template<typename, typename, typename> class T2
>
struct A
{

   A()
   {
      std::cout << Type<A>::name() << std::endl;
   }
};


template<typename T1, typename T2 = void, typename T3 = void>
struct B : public A<One, B >,
           public A<Two, B >
{
   B()
   {
      std::cout << Type<B>::name() << std::endl;
   }
};

template<typename T1, typename T2, typename T3 = void>
struct C : public A<One, C >,
           public A<Two, C >
{
   C()
   {
      std::cout << Type<C>::name() << std::endl;
   }
};

template<typename T1, typename T2, typename T3>
struct D : public A<One, D >,
           public A<Two, D >
{
   D()
   {
      std::cout << Type<D>::name() << std::endl;
   }
};



int main(int argc, char* argv[])
{
   B<int> b;
   C<int,int> c;
   D<int,int,int> d;
   return 0;
}

这个程序打印:

A<One, B>
A<Two, B>
B<int, void, void>
A<One, C>
A<Two, C>
C<int, int, void>
A<One, D>
A<Two, D>
D<int, int, int>

的确,更通用的解决方案会给您带来不同类型的“冗余”,在 那些多余的默认模板参数的形式。但是你 可能会觉得它不那么令人讨厌。

(gcc 5.1/clang 3.6, C++03)

【讨论】: