简体   繁体   English

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

[英]size() of std::array pointer in constexpr context

Let's say I have a function like: 假设我的功能如下:

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

  [... some code ...]
}

clearly the size of the buffer can be evaluated at compile time: data has a constexpr size of 8 elements, 8 * 2 = 16 bytes. 显然,可以在编译时评估缓冲区的大小:数据的constexpr大小为8个元素,8 * 2 = 16个字节。

However, when compiling with -Wall , -pedantic and -std=c++11 I get the infamous error: 但是,当使用-Wall-pedantic-std=c++11编译时,我得到了臭名昭着的错误:

warning: variable length arrays are a C99 feature [-Wvla-extension] 警告:可变长度数组是C99功能[-Wvla-extension]

which I believe makes sense: array::size() is constexpr , but it is still a method, and in the function above we still have to dereference a pointer, which is not constexpr . 我认为有道理: array::size()constexpr ,但它仍然是一个方法,在上面的函数中我们仍然需要取消引用一个指针,这不是constexpr

If I try something like: 如果我尝试类似的东西:

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

gcc (tried version 5.2.0) seems happy: there is no warning. gcc (试用版5.2.0)似乎很高兴:没有警告。

But with clang++ (3.5.1) I still get a warning complaining about variable length arrays. 但是对于clang++ (3.5.1),我仍然会收到一个抱怨可变长度数组的警告。

In my case, I can't easily change the signature of test , it has to take a pointer. 在我的情况下,我不能轻易改变test的签名,它必须采取指针。 So... a few questions: 那么......几个问题:

  1. What is the best / most standard way to get the size of a std::array pointer in constexpr context? 在constexpr上下文中获取std::array 指针大小的最佳/最标准方法是什么?

  2. Is the difference in behavior with pointers vs references expected? 预期指针与引用的行为有何不同? Which compiler is right about the warning, gcc or clang ? 哪个编译器对警告, gccclang是正确的?

I do not know about 2. 我不知道2。

But for 1, we can do this: 但对于1,我们可以这样做:

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

then: 然后:

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

alternatively: 或者:

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

finally, a C++14 cleanup: 最后,一个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 . 实例

The good old C way would be a define, but C++ has const int or for C++11 constexpr . 好的旧C方式是定义,但C ++有const int或C ++ 11 constexpr So if you want the compiler to be aware that the size of the array is a compile time constant, the most portable(*) way would be to make it a const or 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;
}

It compiles without a warning, even with -Wall -pedantic under Clang 3.4.1 它在没有警告的情况下编译,即使在Clang 3.4.1下使用-Wall -pedantic

For the second question, I cannot imagine why gcc make that difference between pointers and refs here. 对于第二个问题,我无法想象为什么gcc在这里指出和引用之间的区别。 Either it can determine that size() method on an std::array whose size is a constant is a constant expression - and it should allow both - or it cannot - and it should emit same warning on both. 它可以确定大小为常量的std::array上的size()方法是一个常量表达式 - 它应该允许两者 - 或者它不能 - 并且它应该在两者上发出相同的警告。 But it does not only concern the compiler, but also the standard library implementation. 但它不仅涉及编译器,还涉及标准库实现。

The real problem is that pre-C++11 std::array was not part of the standard library, and constexpr is also defined only from C++11 on. 真正的问题是pre-C ++ 11 std :: array不是标准库的一部分, constexpr也是仅从C ++ 11开始定义的。 So in pre-C++11 mode, both compiler process std::array as an extension, but there is no way for the size method to declare its return value to be a constant expr. 所以在pre-C ++ 11模式下,两个编译器都将std :: array作为扩展进程,但是size方法没有办法将其返回值声明为常量expr。 This explains why Clang (and gcc facing a pointer) emits the warning. 这解释了为什么Clang(以及面向指针的gcc)会发出警告。

But if you compile original code in c++11 mode ( -std=c++11 ) you should have no warning, because the standard requires size() method on a std::array to be a constexpr . 但是如果你在c ++ 11模式下编译原始代码( -std=c++11 ),你应该没有警告,因为标准要求std::array上的size()方法是constexpr

(*) The question is about best / most standard ; (*)问题是关于最佳/最标准的 ; I cannot say what is best way, and I cannot define most standard either, so I stick to what I would use if I wanted to avoid portability problems on non C++11 compilers. 我不能说什么是最好的方式,我也无法定义大多数标准 ,所以如果我想避免非C ++ 11编译器上的可移植性问题,我会坚持使用。

What about using std::tuple_size on the decltype of your parameter ? 如何在参数的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 ...]
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM