c++ - std::initializer_list 替代方案

标签 c++ templates c++11 variadic-templates

我正在尝试初始化我的自定义 vector 对象,但没有使用 std::initializer_list。我正在做这样的事情:

template <typename T, std::size_t N>
struct vector
{
  template<std::size_t I = 0, typename ...Tp>
  typename std::enable_if<I == sizeof...(Tp), void>::type
  unpack_tuple(std::tuple<Tp...> const& t)
  {
  }

  template<std::size_t I = 0, typename ...Tp>
  typename std::enable_if<I != sizeof...(Tp), void>::type
  unpack_tuple(std::tuple<Tp...> const& t)
  {
    store[I] = std::get<I>(t);

    unpack_tuple<I + 1, Tp...>(t);
  }

  template<typename ...U>
  vector(U&&... args,
    typename std::enable_if<std::is_scalar<U...>::value, void>::type* = 0)
  {
    unpack_tuple(std::forward_as_tuple(std::forward<U>(args)...));
  }

  T store[N];
};

但编译器不会理解构造函数,除非我删除了我需要的 std::enable_if 参数(因为我不想要非标量参数)。有解决办法吗?

最佳答案

std::is_scalar<U...>::value

问题在于 is_scalar只接受一个类型参数。您需要编写一个组合多个 bool 值的包装器。我还想知道,如果您只想要标量类型,为什么还要使用完美转发——只需按值传递它们。这样,您也不必担心 U当您传递一个左值时被推断为引用。

#include <type_traits>

template<bool B>
using bool_ = std::integral_constant<bool, B>;

template<class Head, class... Tail>
struct all_of
  : bool_<Head::value && all_of<Tail...>::value>{};

template<class Head>
struct all_of<Head> : bool_<Head::value>{};

template<class C, class T = void>
using EnableIf = typename std::enable_if<C::value, T>::type;

// constructor
template<typename... U>
vector(U... args, EnableIf<all_of<std::is_scalar<U>...>>::type* = 0)
{
  unpack_tuple(std::tie(args...)); // tie makes a tuple of references
}

上面的代码应该可以工作。但是,作为建议,如果您想要什么,static_assert你不明白,也不要为此滥用 SFINAE。 :) SFINAE 只应在重载上下文中使用。

// constructor
template<typename... U>
vector(U... args)
{
  static_assert(all_of<std::is_scalar<U>...>::value, "vector only accepts scalar types");
  unpack_tuple(std::tie(args...)); // tie makes a tuple of references
}

您的实际问题就这么多,但我推荐使用 indices trick 解包元组(或一般的可变参数,甚至数组)的更好方法。 :

template<unsigned...> struct indices{};
template<unsigned N, unsigned... Is> struct indices_gen : indices_gen<N-1, N-1, Is...>{};
template<unsigned... Is> struct indices_gen<0, Is...> : indices<Is...>{};

template<unsigned... Is, class... U>
void unpack_args(indices<Is...>, U... args){
  [](...){}((store[Is] = args, 0)...);
}

template<class... U>
vector(U... args){
  static_assert(all_of<std::is_scalar<U>...>::value, "vector only accepts scalar types");
  unpack_args(indices_gen<sizeof...(U)>(), args...);
}

这段代码所做的是“滥用”可变参数解包机制。首先,我们生成一组索引 [0 .. sizeof...(U)-1]然后将此列表与 args 一起逐步扩展.我们将此扩展放在可变参数(非模板)函数参数列表中,因为包扩展只能发生在特定位置,这就是其中之一。另一种可能性是作为本地数组:

template<unsigned... Is, class... U>
void unpack_args(indices<Is...>, U... args){
  int a[] = {(store[Is] = args, 0)...};
  (void)a; // suppress unused variable warnings
}

关于c++ - std::initializer_list 替代方案,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12770743/

相关文章:

c++ - 在抽象基类中使用 unique_ptr 作为私有(private)成员

c++ - 专门化模板类的模板成员函数?

C++ 类 vector 推回错误

c++ - C++11 核心语言是否处理 Singleton Dead Reference?

c++ - 转发参数的可变参数列表

c++ - 并行处理碰撞对

c++ - 您知道 Qt 中的任何词法分析器或词法分析器吗?

c++ - 我怎样才能引用一个函数?

c++ - 为什么我需要在以下情况下使用 std::decay?

代码块中的 C++11 编译问题