10

我正在使用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.5and gcc 5

4

3 回答 3

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)在生产代码中使用来检查初始化列表的大小。

于 2015-10-07T19:21:03.260 回答
3

您可以为您的对象创建一个包装器

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}} }); 

由于使用了已删除的函数,将无法编译。活生生的例子。语法有点笨拙,即使这样我也不确定是否涵盖了所有可能的值初始化情况。

@dpy 指出您可能会省略最里面的代码,因此您可以使用原始代码:function( {{ 1, 2, 3, 4, 5 }} ).

于 2015-10-07T19:00:16.693 回答
2

简单的回答:你不能。

std::array用列表初始化时,它是在做一个aggregate initialization,这里解释当列表大小小于成员数时:

  • 如果初始化器子句的数量小于成员数或初始化器列表完全为空,则其余成员由其大括号或等号初始化器初始化,如果在类定义中提供,否则 (C++14 起)通过空列表执行值初始化。如果引用类型的成员是这些剩余成员之一,则程序格式错误(引用不能进行值初始化)

提供小于大小的列表只是一种合法且可接受的行为,因此编译器不会抱怨任何事情。你的代码:

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

相当于:

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

到编译器。你最好的选择是运行时错误(这可能不是你所希望的)。

于 2015-10-07T18:55:35.153 回答