std::数组指针在constexpr上下文中的Size()
size() of std::array pointer in constexpr context
假设我有一个函数:
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
的签名,它必须接受一个指针。所以…几个问题:
在constexpr上下文中获得
std::array
指针的大小的最佳/最标准方法是什么?指针与引用的行为是否有差异?哪个编译器对
gcc
或clang
的警告是正确的?
我不知道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 ...]
}
生活例子。
好的老C方法是定义,但c++有const int
或c++ 11的constexpr
。所以,如果你想让编译器知道数组的大小是一个编译时常数,最可移植的(*)方法是将其设置为const
或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;
}
即使在Clang 3.4.1下使用-Wall -pedantic
,编译时也没有警告。
对于第二个问题,我无法想象为什么gcc在这里区分指针和参考。它可以确定大小为常量的std::array
上的size()
方法是常量表达式——它应该允许两者——或者不允许——它应该对两者发出相同的警告。但它不仅涉及编译器,还涉及标准库的实现。
真正的问题是c++ 11之前的std::array不是标准库的一部分,而且constexpr
也是从c++ 11开始才定义的。因此,在c++ 11之前的模式中,两个编译器都将std::array作为扩展处理,但是size
方法无法将其返回值声明为常量expr。这就解释了为什么Clang(和gcc面对一个指针)会发出警告。
但是如果你在c++11模式下编译原始代码(-std=c++11
),你应该没有警告,因为标准要求std::array
上的size()
方法必须是constexpr
。
(*)问题是关于最好/最标准的;我不能说什么是最好的方式,我也不能定义最标准的,所以我坚持如果我想避免在非c++ 11编译器上的可移植性问题,我将使用什么。
在参数的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 ...]
}
- lambda参数转换为constexpr技巧,然后获取带链接的数组
- C++,OpenCV,尝试显示图像时"OpenCV(4.3.0) Error: Assertion failed (size.width>0 && size.height>0)"此错误
- 多成员Constexpr结构初始化
- 条件constexpr函数
- 大于65535的C++数组[size]引发不一致的溢出
- constexpr 函数中的非文字(通过 std::is_constant_evaluated)
- Visual C++ constexpr Hints
- 为什么(-1)%vector::size()总是返回0
- 如何确认我的constexpr表达式实际上已经在编译时执行
- 在for循环中使用auto vs decltype(vec.size())来处理字符串的向量
- 为什么constexpr的性能比正常表达式差
- 是否可以使用if constexpr删除控制流语句
- 要与"if constexpr"一起使用的编译时消息(在预处理器之后)
- 为什么std::isnan 不是 constexpr?
- Constexpr替代了新的放置方式,可以让内存中的对象保持未初始化状态
- 当一个值是非常量但用常量表达式初始化时使用constexpr
- 使用 constexpr 作为 std::array size
- 检测 C++ 14 中的 constexpr size() 成员函数
- 为什么 std::array::size constexpr 具有简单类型(int、double、..),而不是 std
- std::数组指针在constexpr上下文中的Size()