在constexpr上下文中,std::array指针的size()是多少?

11

假设我有这样一个函数:

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

  [... some code ...]
}

很明显,缓冲区的大小可以在编译时计算出:数据constexpr的大小为8个元素,即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. 指针与引用之间的行为差异是否符合预期?哪个编译器对警告是正确的,gcc还是clang


1
很有趣想知道为什么 size 不是静态成员函数。 - dyp
1
我还应该补充一点,可能有一种通过模板获取大小的方法,类似于:`template std::size_t arraysize(const std::array& array) { return N; }`可以在上面使用。但是...这是正确的方式吗?看起来很复杂。 - rabexc
也许clang抱怨是因为引用是通过const指针实现的?所以即使有深度引用,编译器仍然需要对其进行解引用? - Pumkko
1
能否像这样做 std::tuple_size<decltype(*arr)>::value - tforgione
1
@DragonRock 你需要添加一个 remove_reference_t,这会使它变得相当长。 - dyp
显示剩余4条评论
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 ...]
}

实例演示


@dyp 如果你想的话,我可以添加volatile关键字? ;) - Yakk - Adam Nevraumont

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会在指针和引用之间产生差异。它可以确定一个大小为常量的std::array上的size()方法是一个常量表达式,因此应该允许两者,或者它不能这样做,并且应该在两者上发出相同的警告。但这不仅涉及编译器,还涉及标准库实现。

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

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

(*) 这个问题涉及到“最佳/最标准”的方式;我无法确定什么是“最佳”方式,也无法定义“最标准”的方式,因此我坚持使用如果我想要避免在非C++11编译器上出现可移植性问题时所使用的方式。

但是,如果您在c++11模式下编译原始代码(-std=c++11),则不应该有警告,因为标准要求std::array上的size()方法为constexpr。一个 constexpr(成员)函数在调用时不需要产生常量表达式。 - dyp
1
这是一个例子:struct foo { int i; constexpr int bar() { return i; } }; int main() { foo f{rand()}; constexpr auto x = f.bar(); } - dyp
当使用 -std=c++11 编译时,clang会报错:*错误:constexpr变量'x'必须由常量表达式初始化。constexpr修饰符在bar方法上表示,只要对象已用常量表达式初始化,其结果将是常量表达式。在std::array中,大小应为常量表达式,因此方法 constexpr size_type size() const noexcept; 是必需的 返回常量表达式,符合C++11编译器不应发出任何警告。 - Serge Ballesta
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - Serge Ballesta
@dyp:你的例子与第7.1.5节“constexpr指定符[dcl.constexpr]§5”中的第一个例子相似:“constexpr int f(bool b) { return b ? throw 0 : 0; } // OK constexpr int f() { return f(true); } // ill-formed, no diagnostic required”(假设你的意思是constexpr int bar(int i) ...)。使用bar(21)作为常量表达式将导致程序不合法。由于标准明确表示不需要进行诊断,因此我们只能调用未定义的行为 :-( - Serge Ballesta
显示剩余5条评论

0

你考虑使用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 ...]
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接