避免在专用模板中复制代码

时间:2013-06-07 13:14:56

标签: c++ templates

我想要两个相似的模板,一个带有1个参数,另一个带有2个参数:

template<typename T1, typename T2=void>
struct foo {
  T1 m_t1;
  T2 m_t2;
  foo(T1 t1, T2 t2) : m_t1(t1), m_t2(t2) {}
  T1 t1() { return m_t1; }
  T2 t2() { return m_t2; }
};

template<typename T1>
struct foo<T1,void> {
  T1 m_t1;
  foo(T1 t1) : m_t1(t1) {}
  T1 t1() { return m_t1; }
};

请注意所有与T1相关的内容的代码重复。我怎么能避免这个?

5 个答案:

答案 0 :(得分:7)

唯一的解决方案是在基类中拥有尽可能多的代码。例如:

template<typename T1>
struct foo_base {
    T1 m_t1;
    explicit foo_base(T1 t1) : m_t1(t1) {}
    T1 t1() const { return m_t1; }
};

template<typename T1, typename T2=void>
struct foo : foo_base<T1> {
    T2 m_t2;
    foo(T1 t1, T2 t2) : foo_base<T1>(t1), m_t2(t2) {}
    T2 t2() const { return m_t2; }
};

template<typename T1>
struct foo<T1,void> : foo_base<T1> {
    explicit foo(T1 t1) : foo_base<T1>(t1) {}
};

答案 1 :(得分:2)

这是一个非常笼统的问题。

在这种情况下,您可以将T1相关的东西放在基类中。一般来说,你应该避免一两个问题。你在这里做的也可以通过std::tuple实现,它也是Boost库的一部分。

对于它的价值,tuple通过编写任意数量的基类来工作。

答案 2 :(得分:2)

仔细查看您的代码,您将重新实现std::tuple。交换免费功能t1的{​​{1}}和t2方法,您拥有std::get<N>给您的一切(也许更多)。为方便起见,如果 是一种方法,请考虑以下几点:

std::tuple

对于template<typename... Ts> struct foo { typedef std::tuple<Ts...> Tup; Tup m_ts; foo(Ts... ts) : m_ts{ts...} {} //! template <unsigned N> std::tuple_element<N, Tup> t() { return std::get<N>(Tup); } }; :当然,您可以将该构造函数设为(可变参数)模板,并将参数转发给元组。哦,访问器可能/应该为const和nonconst重载并返回对元组元素的适当引用...

但严重的是,这不值得汗流。背。只需使用普通//!即可。当然,除了你过度简化问题,你做的事情与你告诉我们的不同。

答案 3 :(得分:0)

继承可以解决您的问题。定义一个提供T1内容的单参数基类,并使双参数版本继承该内容。

答案 4 :(得分:0)

有三个数字:0,1和无穷大。

哦,计数从0开始,而不是1!

template<typename... Ts>
struct first_type {}
template<typename T0, typename... Ts>
struct first_type {
  typedef T0 type;
};
template<typename... Ts>
using FirstType = typename first_type<Ts...>::type;

template<typename T0, typename Rest, typename=void>
struct foo_impl;

template<typename... Ts>
struct foo_augment {};
template<typename T1>
struct foo_augment<T1> {
  T1 m_t1;
  T1 t1() const { return m_t1; }
  T1 t1() { return m_t1; }
};

template<typename T0, typename... Ts>
struct foo_impl< T0, std::tuple<Ts...>, typename std::enable_if< (sizeof...(Ts)<2) >::type >:
  foo_augment<Ts...>
{
  // use FirstType<Ts...> to get at the second type of your argument pack
  foo_impl( T0 t0, Ts... ts ):
    m_t0(t0), foo_augment<Ts...>(ts...)
  {};
  T0 m_t0;
  T0 t0() { return m_t0; }
  T0 t0() const { return m_t0; }
};
template<typename T0, typename... Ts>
using foo = foo_impl<T0, std::tuple<Ts...>>;

现在,请注意上面有很多样板文件,比您使用的重复代码数量还要多。

而不是...混乱,您可以使用T1的“保留值”来表示“不存在”,例如void。在这种情况下,您可以使用构造函数:

  template<typename... Ts, typename=typename std::enable_if< ((sizeof...(Ts)==0) == (std::is_same<T1, void>::value)) && (sizeof...(Ts)<2) >::type >
  foo_impl( T0 t0, Ts&&... ts ):
    m_t0(t0), foo_augment<Ts...>(std::forward<Ts>(ts)...)
  {};

其中构造函数是variardic,但SFINAE意味着Ts...参数包必须是0个元素iff T1void,并且必须是1个元素iff T1是不是void

(代码尚未编译,但基本设计应该是合理的)。