11

假设我有一个类似的功能:

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()is 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. 指针与引用的行为差异是预期的吗?哪个编译器对警告是正确的,gcc或者clang

4

3 回答 3

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 ...]
}

活生生的例子

于 2015-10-06T15:03:09.617 回答
0

std::tuple_size在你的参数的 decltype 上使用怎么样?

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 ...]
}
于 2015-10-07T07:01:33.330 回答
0

好的旧 C 方式将是一个定义,但 C++ 有const intor for C++11 constexpr。因此,如果您希望编译器知道数组的大小是编译时间常数,那么最便携(*)的方法是将其设为constor constexpr

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

它编译时没有警告,即使-Wall -pedantic在 Clang 3.4.1 下

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

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

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

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

于 2015-10-06T15:12:32.303 回答