7

我想要两个类似的模板,一个有 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 相关的代码重复。我怎样才能避免这种情况?

4

5 回答 5

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) {}
};
于 2013-06-07T13:18:42.897 回答
2

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

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

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

于 2013-06-07T13:18:34.233 回答
2

仔细查看您的代码,您正在重新实现std::tuple. 将t1t2方法换成免费功能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 重载,并返回对元组元素的适当引用......

但说真的,这不值得流汗。只需使用普通std::tuple的 . 当然,除了你把问题简单化了,你做的事情与你告诉我们的不同。

于 2013-06-07T13:54:38.483 回答
0

继承会解决你的问题。定义一个提供这些T1东西的单参数基类,并使双参数版本继承它。

于 2013-06-07T13:17:38.207 回答
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)...)
  {};

其中构造函数是可变的,但 SFINAE 意味着Ts...参数包必须是 0 个元素 iff T1is void,并且必须是 1 个元素 iff T1is not void

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

于 2013-06-07T13:36:36.717 回答