繁体   English   中英

完全专业化的类作为模板函数参数

[英]Fully specialized class as template function parameter

我编写了两个不同的容器类,它们具有相同的接口,但使用不同的成员数据和算法来操作其成员。 我还有一个模板函数,它接受一个容器并进行一些有用的计算:

class Container1
{
  // implementation here
};

class Container2
{
  // implementation here
};

template<typename ContainerType>
void my_function(ContainerType const& container, /* other parameters */)
{
 // ...
}

困扰我的是'my_function'应该只接受Container1Container2这一事实,但代码不表示这一点,因为ContainerType可以是任何类型。 该函数由容器类型模板化,因为无论容器的内部实现是什么,它都会做同样的事情。 我正在考虑一个变体,其中Container1Container2将是模板类的完全特化。 然后我可以更具体地讨论my_function的参数:

template<typename T>
class Container;

// Tags to mark different container types
struct ContainerType1 { };
struct ContainerType2 { };

template<>
class Container<ContainerType1>
{
  // implementation
};

template<>
class Container<ContainerType2>
{
  // implementation
};

template<typename T>
void my_function(Container<T> const& container, /* other parameters */)
{
}

在第一种情况下,如果'ContainerType'没有my_function所需的接口,则使用错误模板参数的编译将失败,这不是非常有用的信息。 在第二种情况下,如果我提供除Container<ContainerType1>Container<ContainerType2>之外的任何东西,我也会得到编译器错误(模板参数扣除失败),但我更喜欢它,因为它提供了关于什么样的模板参数的提示是期待。

你对此有何看法? 这是一个好的设计理念吗? 您认为值得更改代码吗? 代码中还有许多其他函数,比如my_function ,有时它们所期望的模板参数类型并不明显。 我的其他选项有哪些让my_function更具体? 我知道Boost Concept Check Library的存在。 为了论证,让我们假设我不想通过使用继承和虚函数来解决问题。 如果它与讨论相关,则使用CRTP强制Container1Container2的公共接口。 将来可能会有更多的容器类。

这种问题有一些解决方案。

您的解决方案(将您的类型作为template专业化实现)是一个,但我并不特别喜欢。

另一个是CRTP:

template<typename T>
struct Container {
  // optional, but I find it helpeful
  T* self() { return static_cast<T*>(this); }
  T const* self() const { return static_cast<T const*>(this); }

  // common code between every implementation goes here.  It accesses itself through self(), never this
};

class ContainerType1: public Container<ContainerType1> {
  // more details
};
class ContainerType2: public Container<ContainerType2> {
  // more details
};

这是CRTP的核心。

然后:

template<typename T>
void my_function(Container<T> const& container_, /* other parameters */)
{
  T const& container = *(container.self());
}

和鲍勃是你的叔叔。 作为奖励,这提供了放置公共代码的地方。

另一个选项是标记traits类,它标记了您要支持的类型,例如iterator_traits

template<typename T>
struct is_container : std::false_type {};
template<>
struct is_container<ContainerType1> : std::true_type {};
template<>
struct is_container<ContainerType2> : std::true_type {};

您甚至可以执行SFINAE样式模式匹配来检测基类型(如迭代器的工作方式)。

现在你的方法可以测试is_container<T>::value ,或者在is_container<T>{}上进行标签调度。

我认为你的第一个版本是可行的。

在一天结束时,您总是必须选择最佳方法。 第二个可能看起来像一个矫枉过正,虽然它得到了重点。 如果Container类都有一个共同的函数(比如说Container1::hasPackage() or Container2::hasPackage() ,你选择在my_function调用它,那么它会直接指出你的意思是调用它的资格是在完成许多这样的项目之后,你将开始以相反的方式阅读模板 - 从模板定义开始 - 看看需要什么样的属性来限定特定的类。

说完这一切之后,也许你的问题更适合Code Review

我在ideone上创建的一个例子是使用你的类,但是为my_function添加了一个成员变量name 当然可能会有类支持name但开发人员也可能会烧掉他的手指几次以实现功能背后的想法。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM