std::array:size()作为gcc 11中的常量

vktxenjb  于 2023-01-26  发布在  其他
关注(0)|答案(2)|浏览(149)

我有一个示例代码

#include <iostream>
#include <array>
#include <bitset>

template <class T, std::size_t N>
class Booth
{
  public:
    int function(const std::array<T, N> & data) {
        std::bitset<data.size()> bit_set{};

        return 0;
    };

  private:
};

int main()
{
    std::cout << "std::array test\n";

    std::array<int, 3> data2{2};
    Booth<int, 3>booth{};
    std::cout << booth.function(data2) << std::endl;

    return 0;
}

它在[OnlineGDB][1]和旧版本的gcc中编译得很好,但是在gcc11中开始失败:

g++ -Wall -Wconversion -lstdc++ -pthread -std=c++17 -o array_size src/array_size.cpp
src/array_size.cpp: In function ‘int function(const std::array<int, 42>&)’:
src/array_size.cpp:22:28: error: ‘data’ is not a constant expression
   22 |     std::bitset<data.size()> bit_set{};
      |                            ^
src/array_size.cpp:22:26: note: in template argument for type ‘long unsigned int’
   22 |     std::bitset<data.size()> bit_set{};
      |                 ~~~~~~~~~^~

代码是一样的。语言版本是一样的。我知道如何修复这个错误。我只是想知道编译器中有什么变化?这个变化和失败的原因是什么?

tv6aics1

tv6aics11#

该代码直到最近才生效。如果编译器以前允许它而不进行诊断,那么编译器当时就不符合标准。
问题很简单,function中的data是一个引用,如果它是按值取的,那就没问题了。
按照模板参数的要求,取消data.size()等表达式作为常量表达式的资格的规则之一是,表达式中命名的任何引用(并且其生存期没有在常量表达式的求值期间开始)本身用常量表达式初始化,即使没有对被引用对象应用左值到右值的转换,并且被引用对象的标识也不相关。函数参数是't用常量表达式初始化,因此data不能以任何容量用在函数内的常量表达式中。
没有什么好的理由让这个限制这么严格,你只需要调用.size(),它只返回一个由类型决定的常量,它不需要任何关于调用它的实际对象的信息。
因此,在C23的最新草案中,这一限制已经放宽,允许这样的使用,并且该修复也将被视为与之前的C版本相比的缺陷报告。因此,在未来,编译器应该再次允许您的代码,而不管您选择的是什么C++版本。

nukf8bse

nukf8bse2#

在C++17和std::array类模板中,std::array类模板提供了一个size()成员函数来返回数组的大小。这个函数是constexpr,这意味着它可以在编译时使用,并且大小的值可以用作模板参数。
代码中的问题是std::bitset构造函数需要unsigned long long作为其参数,而不是size_t。您可以使用static_cast<unsigned long long>()size_t显式转换为unsigned long long,而不是使用data.size(),如下所示:

std::bitset<static_cast<unsigned long long>(data.size())> bit_set{};

这将允许代码使用GCC 11和更高版本进行编译。

相关问题