有没有办法强制 std::array 的完全初始化
Is there a way to enforce full initialization of std::array
我正在使用 std::array<size_t, N>
(N 是一个固定的模板变量)。
#include<array>
template<size_t N>
struct A{
size_t function(std::array<size_t, N> arr){ return arr[N-1];} // just an example
};
int main(){
A<5> a;
a.function({{1,2,3,4,5}}));
}
而且效果很好。问题是其他代码被默默地允许:
A.function({{1,2,3}}));
也就是说,即使缺少元素,array
也会以某种方式初始化,即使它定义明确(例如,剩余元素初始化为零,我不确定)这可能是错误来源。
有没有办法强制对额外元素进行初始化? 例如通过生成编译器错误或警告。
我考虑过的一个选择是使用 initializer_list
size_t function2(std::initializer_list<size_t> il){ assert(il.size() == N); ...}
问题在于,这充其量会产生运行时错误并在每次调用时进行检查。我更喜欢编译器 error/warning。
我对 std::array<>{}
的默认初始化并没有那么在意,而是 不完整的 初始化。 (也许对此无能为力,因为这是从 T[N]
静态数组的行为继承而来的。)
我尝试使用 clang 3.5
和 gcc 5
。
简单的回答:你不能。
当用列表初始化std::array
时,它正在做一个aggregate initialization
,当列表大小小于成员数时解释here:
- 如果初始化器子句的数量少于成员的数量或初始化器列表完全为空,则其余成员由其括号或等于初始化器初始化,如果在 class 定义中提供,否则 (C++14 起) 为执行值初始化的空列表。如果引用类型的成员是这些剩余成员之一,则程序格式错误(引用不能被值初始化)
提供小于大小列表是合法且可接受的行为,因此编译器不会抱怨任何事情。您的代码:
A<5> a;
a.function({{1,2,3}}));
相当于:
A<5> a;
a.function({{1,2,3,0,0}}));
编译器。你最好的选择是运行时错误(这可能不是你想要的)。
您可以为您的对象创建一个包装器
template <class T>
struct WrapT
{
WrapT() = delete;
WrapT(T e) : value(e){}
public: T value;
// or add some operator()
};
和
size_t function(std::array<WrapT<size_t>, N> arr){ return arr[N-1].value;}
这样的函数调用(带有完整的大括号初始化)
function({ {{1}, {2}, {3}, {4}} });
由于使用了删除的函数,将无法编译。 Live example。语法有点笨拙,即便如此我也不确定是否涵盖了所有可能的值初始化情况。
@dpy 指出你可能会忽略最里面的那些,所以你得到了你的原始代码:function( {{ 1, 2, 3, 4, 5 }} )
.
您可以使用参数包强制执行此操作,但语法略有不同:
#include <array>
template<size_t N>
struct A{
template<typename... T>
size_t function(T&&... nums)
{
static_assert(sizeof...(nums) == N, "Wrong number of arguments");
std::array<size_t, N> arr = { std::forward<size_t>(nums)... };
return arr[N-1];
}
};
int main(){
A<5> a;
a.function(1,2,3,4,5); // OK
a.function(1,2,4,5); // Compile-time error
}
但是,我认为没有什么好的方法可以在编译时强制执行它。我只会在生产代码中使用 assert(il.size() == N)
来检查初始化列表的大小。
我正在使用 std::array<size_t, N>
(N 是一个固定的模板变量)。
#include<array>
template<size_t N>
struct A{
size_t function(std::array<size_t, N> arr){ return arr[N-1];} // just an example
};
int main(){
A<5> a;
a.function({{1,2,3,4,5}}));
}
而且效果很好。问题是其他代码被默默地允许:
A.function({{1,2,3}}));
也就是说,即使缺少元素,array
也会以某种方式初始化,即使它定义明确(例如,剩余元素初始化为零,我不确定)这可能是错误来源。
有没有办法强制对额外元素进行初始化? 例如通过生成编译器错误或警告。
我考虑过的一个选择是使用 initializer_list
size_t function2(std::initializer_list<size_t> il){ assert(il.size() == N); ...}
问题在于,这充其量会产生运行时错误并在每次调用时进行检查。我更喜欢编译器 error/warning。
我对 std::array<>{}
的默认初始化并没有那么在意,而是 不完整的 初始化。 (也许对此无能为力,因为这是从 T[N]
静态数组的行为继承而来的。)
我尝试使用 clang 3.5
和 gcc 5
。
简单的回答:你不能。
当用列表初始化std::array
时,它正在做一个aggregate initialization
,当列表大小小于成员数时解释here:
- 如果初始化器子句的数量少于成员的数量或初始化器列表完全为空,则其余成员由其括号或等于初始化器初始化,如果在 class 定义中提供,否则 (C++14 起) 为执行值初始化的空列表。如果引用类型的成员是这些剩余成员之一,则程序格式错误(引用不能被值初始化)
提供小于大小列表是合法且可接受的行为,因此编译器不会抱怨任何事情。您的代码:
A<5> a;
a.function({{1,2,3}}));
相当于:
A<5> a;
a.function({{1,2,3,0,0}}));
编译器。你最好的选择是运行时错误(这可能不是你想要的)。
您可以为您的对象创建一个包装器
template <class T>
struct WrapT
{
WrapT() = delete;
WrapT(T e) : value(e){}
public: T value;
// or add some operator()
};
和
size_t function(std::array<WrapT<size_t>, N> arr){ return arr[N-1].value;}
这样的函数调用(带有完整的大括号初始化)
function({ {{1}, {2}, {3}, {4}} });
由于使用了删除的函数,将无法编译。 Live example。语法有点笨拙,即便如此我也不确定是否涵盖了所有可能的值初始化情况。
@dpy 指出你可能会忽略最里面的那些,所以你得到了你的原始代码:function( {{ 1, 2, 3, 4, 5 }} )
.
您可以使用参数包强制执行此操作,但语法略有不同:
#include <array>
template<size_t N>
struct A{
template<typename... T>
size_t function(T&&... nums)
{
static_assert(sizeof...(nums) == N, "Wrong number of arguments");
std::array<size_t, N> arr = { std::forward<size_t>(nums)... };
return arr[N-1];
}
};
int main(){
A<5> a;
a.function(1,2,3,4,5); // OK
a.function(1,2,4,5); // Compile-time error
}
但是,我认为没有什么好的方法可以在编译时强制执行它。我只会在生产代码中使用 assert(il.size() == N)
来检查初始化列表的大小。