c++ - 清理大量使用模板参数

标签 c++ templates

我在项目 A 和 B 使用的日志框架中有一组类。我正在重构框架,以便它可以在项目 B 和 C 中使用。重构主要包括为所有内容提供模板参数:项目 A 可能会运行在 STL 实现较差/没有 STL 实现的嵌入式设备上,而 B 和 C 仅在 pc 上运行,但 B 是单线程而 C 使用多线程。

这很好用,但在我看来会导致大量模板参数和相当难看的 typedef 困惑。我需要大约 20 行来对我将要使用的所有类进行 typedef,还有很多类采用它们自己不使用的模板参数,但需要能够对它们使用的另一个类进行 typedef(这不是本身是一件坏事,但最终一切都开始变得非常复杂)。另一个问题是,当我想向 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;
};

我可以想出一些方法来清理它,但我很难决定使用哪一种或哪种组合。 StackOverFlow,我们将不胜感激!

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

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

这使它变得更简单,但它不是同时隐藏了消息的实际内容吗?假设用户想要提供一个实现,他没有直接看到消息必须使用某种字符串类型等。

我想到的另一种技术,但不记得以前在某个地方见过,这让它看起来很可疑,它只是在单个结构中定义所有内容并将其作为单个模板参数传递给所有内容:

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;
};

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

最佳答案

“其他”技术在 C++ 中很常见。参数类通常称为“ trait class ”。

这是要走的路(为什么它会给你一种不安的感觉?)。它广泛用于 Boost 库和其他 C++ 库中。甚至标准库也使用它,例如在std::basic_string类。


元函数是一个同样成熟的替代方案。从最基本的角度来说,元函数是一种对类型而不是对象进行操作的“函数”。因此,在函数接受值参数并返回值的地方,元函数接受模板参数并“返回”一个类型:

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

像普通类型定义一样使用(“调用”)元函数。

typedef identity<int>::type mytype; // or
identity<int>::type x;

在这种情况下不是很有用。但请考虑以下通用元功能:

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

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

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

typename remove_const<T>::type& _reference;

(typename 是必需的,因为 T 是一个模板参数,这使得 remove_const<T>::type 成为一个依赖类型。您上面的示例代码实际上省略了很多必需的 typename s – 它无法在多种现代编译器上编译!)

现在,如何将其应用到您的问题中?

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

struct Embedded { };
struct Compliant { };

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

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;
};

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

ThreadedMessageAcceptor<Compliant> x;

该类使用以下元函数:

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

template <typename T, typename Alloc, typename Spec>
struct ContainerB { };

您需要记住根据您的目标规范适本地专门化它们,例如:

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;
};

这已经表明,除了 Spec 之外,元函数可以有任意多个参数。 (我一时兴起把它放在最后 - 但它的位置应该是一致的)。

可以肯定的是,这比使用单个特征类时的代码更多,但它的内聚性较低,逻辑上分离关注点并且更容易重用。

关于c++ - 清理大量使用模板参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3422563/

相关文章:

templates - Magento:基于属性集的模板

继承模板类中的 C++ 编译器错误

c++ - 在 C++ 模板中使用声明?

c++ - 在 C++ 中,为什么不能使用另一个类的模板类型来为模板类成员函数加好友?

C++ 中的模板初始化

c++ - 断言和单元测试不兼容吗?

Android NDK 获取二进制文件的 md5

c++ - 二维数组的 cudamalloc

c++ - std::transform 中未解析的重载函数类型

c++ - 什么时候使用模板显式实例化?