函数中 `std::initializer_list<T>::size` 上的 `static_assert`
`static_assert` on `std::initializer_list<T>::size` in a function
在我的单元测试中,我想要一种快速且(干净的)肮脏的方法来将值分配给 initializer_list
中的静态大小的 C 数组。我不是一个完整的野兽,所以我想 static_assert
尺寸是一样的。我写了一个辅助函数 set_array
来做到这一点:
template <typename T, std::size_t N>
constexpr void set_array(T (&x)[N], std::initializer_list<T>&& list) {
assert(list.size() == N); // why can't I static_assert in C++17?
for (std::size_t i = 0; i < N; ++i)
x[i] = list.begin()[i];
}
打算将其用作 set_array(foo, {1, 2, 3, 4});
,foo 声明为 int foo[4]
。
我使用的是 C++17,所以 std std::initializer_list<T>::size
是 constexpr,但是一旦我通过函数调用传递列表,我就失去了将其视为 constexpr 的特权,因为我不能将函数参数约束为 constexpr.
感觉应该有一个我没有看到的简单解决方案。当然,我可以想象我可以玩一些反常的元编程游戏来对类型中的大小进行编码,但这是一个简单的小帮手,应该使事情变得干净和可读,我不想发疯。
问题:是否有简单的解决方案,或者我应该只接受运行时断言? (是的,我知道如果给我一个简单的解决方案,我会因为自己没有看到它而感到愚蠢。)认为我正在以错误的方式进行吗?没关系,我愿意接受建议并感谢批评。
详情:
为了完整起见,这里是编译器错误和版本信息。在 Clang 版本 8.0.0-3(Ubuntu clang-8 附带)中,我得到:
error: static_assert expression is not an
integral constant expression
static_assert(list.size() == N);
^~~~~~~~~~~~~~~~
在 GCC 8.3.0 中我得到了类似的错误,另外告诉我 list
不是常量表达式。
函数的参数不是常量。
这在 C++ 中是合法的:
void foo( bool which ) {
std::initializer_list<int> a = {1,2,3};
std::initializer_list<int> b = {1,2,3,4};
int x[4];
std::initializer_list<int> c = which?a:b;
set_array(x, c);
}
试试这个:
template<class T>
struct block_deduction_t{using type=T;};
template<class T>
using block_deduction = typename block_deduction_t<T>::type;
template <typename T, std::size_t N>
constexpr void set_array(T (&x)[N], block_deduction<T const(&)[N]> list) {
for (std::size_t i = 0; i < N; ++i)
x[i] = list[i];
}
现在这允许您省略 尾随元素,但它们将被零初始化。
再一次,这个解决方案:
template <typename T, std::size_t N>
constexpr void set_array(T (&x)[N], T const(&list)[N]) {
for (std::size_t i = 0; i < N; ++i)
x[i] = list[i];
}
实际上阻挡了尺寸过小的右侧。所以可能会更好。
语法与您的完全一致。
如果您想要一个漂亮的错误消息,并且要转换右侧类型以匹配左侧类型:
template <typename T, std::size_t N, std::size_t M>
constexpr void set_array(T (&x)[N], block_deduction<T> const(&list)[M]) {
static_assert(M==N, "wrong number of elements");
for (std::size_t i = 0; i < N; ++i)
x[i] = list[i];
}
有很多变体。
即使 size
是 constexpr
也会失败的原因是因为 list
是 而不是 constexpr 变量,所以任何成员函数调用它也不会是 constexpr
.
虽然一切都没有丢失。您可以做的是使用 std::array
而不是 std::initializer_list
,这样您甚至可以摆脱 static_assert
,例如:
template <typename T, std::size_t N>
constexpr void set_array(T (&x)[N], std::array<T, N>&& list) {
for (std::size_t i = 0; i < N; ++i)
x[i] = list[i];
}
int main()
{
int arr[4];
set_array(arr, {1,2,3,4});
std::cout << arr[3];
}
如果您尝试使用
set_array(arr, {1,2,3,4,5});
然后你会得到一个编译器错误,比如
main.cpp:12:16: note: candidate function [with T = int, N = 4] not viable: cannot convert initializer list argument to 'std::array<int, 4UL>'
constexpr void set_array(T (&x)[N], std::array<T, N>&& list) {
在我的单元测试中,我想要一种快速且(干净的)肮脏的方法来将值分配给 initializer_list
中的静态大小的 C 数组。我不是一个完整的野兽,所以我想 static_assert
尺寸是一样的。我写了一个辅助函数 set_array
来做到这一点:
template <typename T, std::size_t N>
constexpr void set_array(T (&x)[N], std::initializer_list<T>&& list) {
assert(list.size() == N); // why can't I static_assert in C++17?
for (std::size_t i = 0; i < N; ++i)
x[i] = list.begin()[i];
}
打算将其用作 set_array(foo, {1, 2, 3, 4});
,foo 声明为 int foo[4]
。
我使用的是 C++17,所以 std std::initializer_list<T>::size
是 constexpr,但是一旦我通过函数调用传递列表,我就失去了将其视为 constexpr 的特权,因为我不能将函数参数约束为 constexpr.
感觉应该有一个我没有看到的简单解决方案。当然,我可以想象我可以玩一些反常的元编程游戏来对类型中的大小进行编码,但这是一个简单的小帮手,应该使事情变得干净和可读,我不想发疯。
问题:是否有简单的解决方案,或者我应该只接受运行时断言? (是的,我知道如果给我一个简单的解决方案,我会因为自己没有看到它而感到愚蠢。)认为我正在以错误的方式进行吗?没关系,我愿意接受建议并感谢批评。
详情: 为了完整起见,这里是编译器错误和版本信息。在 Clang 版本 8.0.0-3(Ubuntu clang-8 附带)中,我得到:
error: static_assert expression is not an
integral constant expression
static_assert(list.size() == N);
^~~~~~~~~~~~~~~~
在 GCC 8.3.0 中我得到了类似的错误,另外告诉我 list
不是常量表达式。
函数的参数不是常量。
这在 C++ 中是合法的:
void foo( bool which ) {
std::initializer_list<int> a = {1,2,3};
std::initializer_list<int> b = {1,2,3,4};
int x[4];
std::initializer_list<int> c = which?a:b;
set_array(x, c);
}
试试这个:
template<class T>
struct block_deduction_t{using type=T;};
template<class T>
using block_deduction = typename block_deduction_t<T>::type;
template <typename T, std::size_t N>
constexpr void set_array(T (&x)[N], block_deduction<T const(&)[N]> list) {
for (std::size_t i = 0; i < N; ++i)
x[i] = list[i];
}
现在这允许您省略 尾随元素,但它们将被零初始化。
再一次,这个解决方案:
template <typename T, std::size_t N>
constexpr void set_array(T (&x)[N], T const(&list)[N]) {
for (std::size_t i = 0; i < N; ++i)
x[i] = list[i];
}
实际上阻挡了尺寸过小的右侧。所以可能会更好。
语法与您的完全一致。
如果您想要一个漂亮的错误消息,并且要转换右侧类型以匹配左侧类型:
template <typename T, std::size_t N, std::size_t M>
constexpr void set_array(T (&x)[N], block_deduction<T> const(&list)[M]) {
static_assert(M==N, "wrong number of elements");
for (std::size_t i = 0; i < N; ++i)
x[i] = list[i];
}
有很多变体。
即使 size
是 constexpr
也会失败的原因是因为 list
是 而不是 constexpr 变量,所以任何成员函数调用它也不会是 constexpr
.
虽然一切都没有丢失。您可以做的是使用 std::array
而不是 std::initializer_list
,这样您甚至可以摆脱 static_assert
,例如:
template <typename T, std::size_t N>
constexpr void set_array(T (&x)[N], std::array<T, N>&& list) {
for (std::size_t i = 0; i < N; ++i)
x[i] = list[i];
}
int main()
{
int arr[4];
set_array(arr, {1,2,3,4});
std::cout << arr[3];
}
如果您尝试使用
set_array(arr, {1,2,3,4,5});
然后你会得到一个编译器错误,比如
main.cpp:12:16: note: candidate function [with T = int, N = 4] not viable: cannot convert initializer list argument to 'std::array<int, 4UL>'
constexpr void set_array(T (&x)[N], std::array<T, N>&& list) {