清理大量使用模板参数

sti*_*ijn 3 c++ templates

我在项目A和B使用的日志框架中有一组类.我正在重构框架,因此它可以在项目B和C中使用.重构主要包括提供所有模板参数:项目A可以在嵌入式上运行具有差/无STL实现的设备,而B和C只在PC上运行,但B是单线程而C使用多线程.

这很好用,但结果在我看来是一个非常多的模板参数和一个相当丑陋的typedef混乱.我需要20行来键入我将要使用的所有类,并且还有很多类采用模板参数,他们不使用自己,但是需要能够键入dede他们使用的另一个类(这不是本身就是一件坏事,但最终一切都开始变得非常复杂了.另一个问题是,当我想向A类添加一些功能并且需要添加容器时,A类需要额外的模板参数.因此,看到/使用A类的所有其他类突然也需要额外的参数,从而导致多米诺骨牌效应.

略有夸张的例子:

template< class string, class map, class mutex >
class MessageDestination
{
  typedef Message< string, map > message_type;
  virtual void Eat( const message_type& ) = 0;
}

template< class string, class map, class stream >
class MessageFormatter
{
  typedef Message< string, map > message_type;
  virtual void Format( const message_type&, stream& ) = 0;
}

template< class string, class map, class containerA,
          template< class, class > containerB, template< class, class > class queue, class allocator >
class ThreadedMessageAcceptor
{
  typedef Message< string, map > message_type;
  typedef MessageDestination< string, map > destination_type;
  typedef containerB< destination_type, allocator > destinations_type;
  typedef queue< message_type, allocator > messages_type;
};
Run Code Online (Sandbox Code Playgroud)

我可以想一些清理它的技术,但是我很难决定使用哪种或哪种组合.StackOverFlow,您的帮助将不胜感激!

这是我想到的第一个解决方案,将参数一起加入到它们最终形成的类型中:

template< class message, class mutex >
class MessageDestination
{
  virtual void Eat( const message& ) = 0;
}
Run Code Online (Sandbox Code Playgroud)

这使得它更简单,但它不是同时隐藏了什么消息实际上是什么?假设用户想要提供实现,他不会直接看到该消息必须使用某种字符串类型等.

我想知道的另一种技术,但是不记得曾经在somwhere之前看到它让它看起来很可疑,只是在一个结构中定义所有内容并将其作为单个模板参数传递给所有内容:

struct MyTemplateParameters
{
  typedef std::string string;
  typedef std::map map;
  typedef std::queue queue;
  typedef LightMutex mutex;
  template< class A, class B >
  struct DefineContainerB
  {
    typedef containerB< A, B >::type;
  }
  //....
};

template< class parameters >
class MessageDestination
{
  typedef Message< parameters > message_type;
  virtual void Eat( const message_type& ) = 0;
};

template< class parameters >
class ThreadedMessageAcceptor
{
  typedef Message< parameters > message_type;
  typedef MessageDestination< parameters > destination_type;
  typedef parameters::DefineContainerB< destination_type, parameters::allocator >::type destinations_type;
};
Run Code Online (Sandbox Code Playgroud)

这很好,因为它允许在一个单点指定所有内容,并且所有类的typedef都将是类XXX <MyTemplateParameters>,但同样,它给我一种不安的感觉.是否有一个原因?

Kon*_*lph 5

"其他"技术在C++中很常见.参数类通常称为" 特征类 ".

这是要走的路(为什么它给你一种不安的感觉?).它在Boost库和其他C++库中普遍使用.甚至标准库也使用它,例如在std::basic_string课堂上.


同样成熟的替代方案是元功能.在最基本的情况下,元函数是一种"函数",它对类型而不是对象进行操作.因此,当一个函数获取值参数并返回一个值时,元函数会获取模板参数并"返回"一个类型:

template <typename T>
struct identity {
    typedef T type;
};
Run Code Online (Sandbox Code Playgroud)

元函数与普通类型定义一样使用("调用").

typedef identity<int>::type mytype; // or
identity<int>::type x;
Run Code Online (Sandbox Code Playgroud)

在这种情况下不是很有用.但请考虑以下常见元函数:

template <typename T>
struct remove_const {
    typedef T type;
};

template <typename T>
struct remove_const<T const> {
    typedef T type;
};
Run Code Online (Sandbox Code Playgroud)

这可以用于使任意类型(特别是模板参数)非const.这实际上是我目前在项目中使用的类型:我有一个接受const和非const类型的类,并提供适当的接口.但是,在内部我需要存储一个非const引用.很简单,我只在我的班级中使用以下代码:

typename remove_const<T>::type& _reference;
Run Code Online (Sandbox Code Playgroud)

(这typename是必需的,因为它T是一个模板参数,并且它构成remove_const<T>::type一个依赖类型.上面的示例代码实际上省略了很多必需typename的代码 - 它不会在几个现代编译器上编译!)

现在,如何将此问题应用于您的问题?

创建两个空标记类型,指定您的类型是在嵌入式设备上还是在兼容的编译器上使用:

struct Embedded { };
struct Compliant { };
Run Code Online (Sandbox Code Playgroud)

现在您可以根据这些来定义类,例如:

template<typename Spec>
class ThreadedMessageAcceptor
{
    typedef Message< Spec > message_type;
    typedef MessageDestination< Spec > destination_type;
    typedef typename Allocator< destination_type, Spec >::type allocator_type;
    typedef typename ContainerB< destination_type, allocator_type, Spec >::type destinations_type;
};
Run Code Online (Sandbox Code Playgroud)

在这里,Spec将是CompliantEmbedded.因此,要在符合标准的编译器上使用它,请编写:

ThreadedMessageAcceptor<Compliant> x;
Run Code Online (Sandbox Code Playgroud)

该类使用以下元函数:

template <typename T, typename Spec>
struct Allocator { };

template <typename T, typename Alloc, typename Spec>
struct ContainerB { };
Run Code Online (Sandbox Code Playgroud)

您需要记住根据您的目标规范对它们进行适当的专门化,例如:

template <typename T>
struct Allocator<T, Compliant> {
   typedef std::allocator<T> type;
};

template <typename T, typename Alloc>
struct ContainerB<T, Alloc, Compliant> {
    typedef std::vector<T, Alloc> type;
};
Run Code Online (Sandbox Code Playgroud)

这已经表明一个元函数可能有任意多个参数除了Spec(我把它放在最后一个奇思妙想 - 但它的位置应该是一致的).

可以肯定的是,这比使用单个特征类时更多的代码,但它具有较低的内聚力,逻辑上将关注点分开并且更易于重用.