用参数包参数初始化`std :: array`

时间:2019-01-31 10:27:24

标签: c++ variadic-templates template-meta-programming sfinae template-deduction

有结构HasArray与模板参数类型名称T,并为size_t N

template<typename T, size_t N>
struct HasArray {
  // enable_if sizeof...(T) equals N
  template<typename ... Types, typename std::enable_if<sizeof...(Types) == N, int>::type = 0>
  explicit HasArray(Types ... s) : arr { s... } {}
 protected:
  std::array<T, N> arr;
};

我想初始化构件数组arr与构造函数的参数包参数。

HasArray<uint32_t, 2> foo(7, 13);

但是,收益率c++11-narrowing警告在锵。

error: non-constant-expression cannot be narrowed from type 'int' to 'std::__1::array<unsigned int, 2>::value_type' (aka 'unsigned int') in initializer list [-Wc++11-narrowing]

我看到没有办法投所有s“类型号第Types输入T。有一个吗?

编辑,感谢您的所有回答。我最终在压缩参数上使用了static_cast<>,在不可转换时使用了SFINAE:

template<typename T, size_t N>
struct HasArray {
  // Use `static_cast` to construct `arr` with `s`
  // Add `enable_if` all `Types` are `is_convertible`
  template<typename ... Types, 
    typename std::enable_if<sizeof...(Types) == N, int>::type = 0,
    typename std::enable_if<(std::is_convertible<Types, T>::value && ...), int>::type = 0>
  explicit HasArray(Types ... s) : arr { static_cast<T>(s)... } {}
 protected:
  std::array<T, N> arr;
};

3 个答案:

答案 0 :(得分:3)

如果您想从可转换为std::array的任何值构造T,那么最简单的解决方案就是在构造函数中添加static_cast<T>(...)

template<typename T, size_t N>
struct HasArray {
  template<typename ... Types,
           typename std::enable_if<sizeof...(Types) == N, int>::type = 0>
  explicit HasArray(Types ... s) : arr {static_cast<T>(s)... } {}
protected:
  std::array<T, N> arr;
};

https://godbolt.org/z/TEoZOG

在无法进行这种转换的情况下,也可以“构造出”构造函数,但是在我看来,默认错误消息在当前简单情况下会更好,并且您可以在构造函数主体中添加带有更好消息的static_asserts。

答案 1 :(得分:2)

您可以使用中间类:

template <std::size_t, typename T>
using always_t = T;

template <typename T, typename Seq>
struct HasArrayImpl;

template <typename T, std::size_t...Is>
struct HasArrayImpl<T, std::index_sequence<Is...>>
{
    explicit HasArrayImpl(always_t<Is, T> ... v) : arr { v... } {}
protected:
    std::array<T, sizeof...(Is)> arr;
};

template <typename T, std::size_t N>
using HasArray = HasArrayImpl<T, std::make_index_sequence<N>>;

否则,您可以将SFINAE扩展为可转换类型,并明确转换值

template<typename T, size_t N>
struct HasArray {
    // enable_if sizeof...(T) equals N
    template <typename ... Types,
             std::enable_if_t<(sizeof...(Types) == N)
                                   && (std::is_convertible<Types, T>::value && ...),
                              int>::type = 0>
    explicit HasArray(Types ... s) : arr{ static_cast<T>(s)... } {}
 protected:
    std::array<T, N> arr;
};

答案 2 :(得分:1)

  

我看不出要强制所有类型都必须是T类型的方法。有没有?

我不明白,从你的问题,如果你想要的是Types被抵扣模板类型,和所有他们的推导的确切T,或者如果你想要一个无模板构造函数,该构造函数接收N类型的T值。

第一种情况很简单(如果可以使用C ++ 17模板折叠;否则要稍微复杂一点),因为可以使用std::is_same

template <typename ... Types,
          typename std::enable_if<(sizeof...(Types) == N) 
              && (... && std::is_same<Types, T>::value), int>::type = 0>
explicit HasArray(Types ... s) : arr {{ s... }}
 { }

有关第二种情况,我建议使用的特化的Jarod42溶液的变形HasArray,而不是中间类(编辑:添加从Jarod42本身的改进;感谢):

template<typename T, std::size_t N, typename = std::make_index_sequence<N>>
struct HasArray;

template<typename T, std::size_t N, std::size_t ... Is>
struct HasArray<T, N, std::index_sequence<Is...>>
 {
   static_assert( sizeof...(Is) == N , "wrong sequence size" );

   protected:
      std::array<T, N> arr;

   public:
      explicit HasArray(getType<T, Is> ... s) : arr {{ s... }}
       { }
 };

getType在哪里

template <typename T, std::size_t>
using getType = T;

在第一种情况下,

HasArray<std::uint32_t, 2> foo(7, 13);

给出编译错误是因为713被推导出为int而不是std::uin32_t

在第二种情况下,它会在HasArray具有构造函数的情况下进行编译

HasArray(std::uint32_t, std::uint32_t)

int被转换为std::uint32_t

以下是第二种情况的完整编译C ++ 14示例

#include <array>
#include <cstdint>
#include <type_traits>

template <typename T, std::size_t>
using getType = T;

template<typename T, std::size_t N, typename = std::make_index_sequence<N>>
struct HasArray;

template<typename T, std::size_t N, std::size_t ... Is>
struct HasArray<T, N, std::index_sequence<Is...>>
 {
   static_assert( sizeof...(Is) == N , "wrong sequence size" );

   protected:
      std::array<T, N> arr;

   public:
      explicit HasArray(getType<T, Is> ... s) : arr {{ s... }}
       { }
 };

int main ()
 {
   HasArray<std::uint32_t, 2> foo(7, 13);
 }