constexpr上下文中std :: array指针的size()

时间:2015-10-06 13:42:25

标签: c++ c++11 gcc clang++ constant-expression

假设我的功能如下:

int test(std::array<char, 8>* data) {
  char buffer[data->size() * 2];

  [... some code ...]
}

显然可以在编译时评估缓冲区的大小:数据的constexpr大小为8个元素,8 * 2 = 16个字节。

但是,在使用-Wall-pedantic-std=c++11进行编译时,我会收到臭名昭着的错误:

  

警告:可变长度数组是C99功能[-Wvla-extension]

我认为有道理:array::size()constexpr,但它仍然是一个方法,在上面的函数中我们仍然需要取消引用一个不是constexpr的指针。

如果我尝试这样的话:

int test(std::array<char, 8>& data) {
  char buffer[data.size() * 2];
  [...]
}

gcc(尝试过5.2.0版)似乎很高兴:没有警告。

但是对于clang++(3.5.1),我仍然会收到一个抱怨变长数组的警告。

在我的情况下,我无法轻易更改test的签名,它必须采用指针。所以...几个问题:

  1. 在constexpr上下文中获取std::array 指针的大小的最佳/最标准方法是什么?

  2. 预期指针与引用的行为有何不同?警告哪个编译器正确,gccclang

3 个答案:

答案 0 :(得分:2)

我不知道2。

但对于1,我们可以这样做:

template<class T, size_t N>
constexpr std::integral_constant<size_t, N> array_size( std::array<T, N> const& ) {
  return {};
}

然后:

void test(std::array<char, 8>* data) {
  using size=decltype(array_size(*data));
  char buffer[size{}];
  (void)buffer;
  // [... some code ...]
}

或者:

template<class T, class U, size_t N>
std::array<T,N> same_sized_array( std::array< U, N > const& ) {
  return {};
}

void test(std::array<char, 8>* data) {
  auto buffer = same_sized_array<char>(*data);
  (void)buffer;
  // [... some code ...]
}

最后,C ++ 14清理:

template<class A>
constexpr const decltype(array_size( std::declval<A>() )) array_size_v = {};

void test3(std::array<char, 8>* data) {
  char buffer[array_size_v<decltype(*data)>];
  (void)buffer;
  // [... some code ...]
}

Live example

答案 1 :(得分:0)

好的旧C方式是定义,但C ++有const int或C ++ 11 constexpr。因此,如果您希望编译器知道数组的大小是编译时常量,那么最便携(*)的方法是使其成为constconstexpr

#include <iostream>
#include <array>

const size_t sz = 8;  // constexpr size_t sz for c++11

int test(std::array<char, sz>* data) {
  char buffer[sz * 2];

  buffer[0] = 0;
  return 0;
}
int main()
{
    std::array<char, sz> arr = { { 48,49,50,51,52,53,54,55 } };
    int cr = test(&arr);
    std::cout << cr << std::endl;
    return 0;
}

即使在Clang 3.4.1

下使用-Wall -pedantic,它也会在没有警告的情况下进行编译

对于第二个问题,我无法想象为什么gcc会在指针和引用之间产生差异。它可以确定大小为常量的size()上的std::array方法是一个常量表达式 - 它应该允许两者 - 或者它不能 - 并且它应该在两者上发出相同的警告。但它不仅涉及编译器,还涉及标准库实现。

真正的问题是pre-C ++ 11 std :: array不是标准库的一部分,而constexpr也只是从C ++ 11开始定义的。因此在pre-C ++ 11模式下,两个编译器都将std :: array作为扩展进程处理,但size方法无法将其返回值声明为常量expr。这解释了为什么Clang(和面向指针的gcc)会发出警告。

但是如果你用c ++ 11模式(-std=c++11)编译原始代码,你应该没有警告,因为标准要求size()上的std::array方法是{{ 1}}。

(*)问题是关于最佳/最标准的;我不能说什么是最好的方式,我也无法定义大多数标准,所以如果我想避免非C ++上的可移植性问题,我会坚持使用11个编译器。

答案 2 :(得分:0)

如何在参数的decltype上使用std::tuple_size呢?

void test(std::array<char, 8>* data) {
    using data_type = std::remove_pointer<decltype(data)>::type;
    char buffer[std::tuple_size<data_type>::value * 2];
    static_assert(sizeof buffer == 16, "Ouch");
    // [... some code ...]
}