使用参数包参数初始化 std::array
Initialise `std::array` with parameter pack arguments
结构 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);
但这会在 Clang 中产生 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]
我看不出有什么办法可以将 Types
类型的所有 s
转换为 T
类型。有吗?
编辑 感谢您的所有回答。当不可转换时,我最终在打包参数和 SFINAE 上使用 static_cast<>
:
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;
};
您可能会使用中级 class:
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;
};
I see no way to enforce that all types Types must be of type T. Is there one?
我不明白,根据你的问题,如果你希望 Types
是可推导的模板类型,并且所有它们都被推导 完全 为 T
,或者如果您想要一个无模板构造函数来接收 T
.
类型的精确 N
值
第一种情况很简单(如果你可以使用 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
的特化而不是中间 class(编辑:添加了 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);
给出编译错误,因为 7
和 13
被推断为 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);
}
如果您想从任何可转换为 T
的值构造 std::array
,那么最简单的解决方案就是在您的构造函数中添加 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;
};
如果无法进行此类转换,也可以 "SFINAE out" 构造函数,但我认为在当前简单情况下默认错误消息会更好,您可以添加 static_asserts 更好构造函数主体中的消息。
结构 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);
但这会在 Clang 中产生 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]
我看不出有什么办法可以将 Types
类型的所有 s
转换为 T
类型。有吗?
编辑 感谢您的所有回答。当不可转换时,我最终在打包参数和 SFINAE 上使用 static_cast<>
:
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;
};
您可能会使用中级 class:
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;
};
I see no way to enforce that all types Types must be of type T. Is there one?
我不明白,根据你的问题,如果你希望 Types
是可推导的模板类型,并且所有它们都被推导 完全 为 T
,或者如果您想要一个无模板构造函数来接收 T
.
N
值
第一种情况很简单(如果你可以使用 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
的特化而不是中间 class(编辑:添加了 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);
给出编译错误,因为 7
和 13
被推断为 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);
}
如果您想从任何可转换为 T
的值构造 std::array
,那么最简单的解决方案就是在您的构造函数中添加 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;
};
如果无法进行此类转换,也可以 "SFINAE out" 构造函数,但我认为在当前简单情况下默认错误消息会更好,您可以添加 static_asserts 更好构造函数主体中的消息。