有没有一种方法可以强制完全初始化std::array?

11

我正在使用 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); ...}

问题在于这将在最好的情况下生成一个运行时错误,在每次调用时进行检查。我更喜欢编译器错误/警告。
我并不太关心std::array<>{}的默认初始化,而是关心不完整的初始化。(也许无法解决,因为这是从T[N]静态数组的行为继承而来。)
我尝试使用clang 3.5gcc 5

@texasbruce,谢谢,我试过了。问题是在std::array<size_t, N>中的N不能从参数中推断出来(至少在我的编译器中),从那里开始std::enable_if也没有帮助。或者你的意思不同? - alfC
我误解了你的问题。enable_if在这里没有帮助。 - SwiftMango
3个回答

7
你可以使用参数包来强制执行,但语法略有不同:

通过使用参数包,您可以实现此目的,但语法有所不同:

#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) 来检查初始化列表的大小。


3
您可以为您的对象创建一个包装器。
template <class T>
struct WrapT
{
    WrapT() = delete;

    WrapT(T e)   : value(e){}

   public: T value; 
   // or add some operator()
};

and

size_t function(std::array<WrapT<size_t>, N> arr){ return arr[N-1].value;}

因此,像这样的函数调用(使用完整的花括号初始化)

function({ {{1}, {2}, {3}, {4}} }); 

由于使用了已删除的函数,代码将无法编译。链接为实时示例。语法有点笨拙,即使如此,我也不确定是否覆盖了所有可能的值初始化情况。
@dpy指出您可以省略最内层的部分,以便获得原始代码:function( {{ 1, 2, 3, 4, 5 }} )

这让我明白问题不仅仅是 std::array,而且还涉及元素类型。这个答案非常概念性,因为它告诉我,我可能应该使用显式初始化类型。在 clang 中,由于某种原因,我不需要在每个元素周围加上额外的括号。 - alfC
WrapT 可以具有自动转换 operator T&()operator T const&() const。此外,我可以定义 template<class T, size_t N> using initialized_array = std::array<WrapT<T>, N>; 并将其用作 ... function(initialized_array<size_t, N> arr){...} - alfC
2
您可以使用花括号省略,这将恢复原始问题中的 {{ 1, 2, 3, 4, 5 }} 语法。第一个 { 初始化 std::array,第二个初始化 std::array 的内部 C 风格数组 (X),然后我们有一个数字 1,它不是 {,因此可以应用花括号省略。即每个 WrapT<size_t> 元素的成员都会从单个初始化程序 12 等中进行初始化。这是因为 WrapT(T e) 允许隐式转换。 - dyp
@dyp 感谢您的回复,已进行更新。由于某些原因,GCC 提示需要完整语法。 - edmz

2
简单回答:你不能。
当使用列表初始化std :: array时,它正在进行聚合初始化。当列表的大小小于成员数量时,这在此处有解释:
  • 如果初始化程序子句的数量小于成员数量或初始化程序列表完全为空,则其余成员将通过其括号或等于初始化器进行初始化(如果在类定义中提供),否则(由于C ++ 14)通过空列表进行值初始化。如果引用类型的成员是这些剩余成员之一,则程序无效(引用无法进行值初始化)。
提供少于大小列表的行为仅仅是合法和可以接受的,因此编译器不会抱怨任何内容。你的代码:
A<5> a;
a.function({{1,2,3}}));

等同于:

A<5> a;
a.function({{1,2,3,0,0}}));

对于编译器来说,最好的选择是运行时错误(可能不符合您的期望)。


我猜运行时检查也不可能,因为例如如何区分实际零和省略的零。改为初始化列表是另一种解决方法。 - alfC
我接受了这个答案,因为它在形式上是正确的,其他答案是不错的解决方法。 - alfC

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接