我有一个示例代码
#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
中编译得很好,但是在gcc
11中开始失败:
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{};
| ~~~~~~~~~^~
代码是一样的。语言版本是一样的。我知道如何修复这个错误。我只是想知道编译器中有什么变化?这个变化和失败的原因是什么?
2条答案
按热度按时间tv6aics11#
该代码直到最近才生效。如果编译器以前允许它而不进行诊断,那么编译器当时就不符合标准。
问题很简单,
function
中的data
是一个引用,如果它是按值取的,那就没问题了。按照模板参数的要求,取消
data.size()
等表达式作为常量表达式的资格的规则之一是,表达式中命名的任何引用(并且其生存期没有在常量表达式的求值期间开始)本身用常量表达式初始化,即使没有对被引用对象应用左值到右值的转换,并且被引用对象的标识也不相关。函数参数是't用常量表达式初始化,因此data
不能以任何容量用在函数内的常量表达式中。没有什么好的理由让这个限制这么严格,你只需要调用
.size()
,它只返回一个由类型决定的常量,它不需要任何关于调用它的实际对象的信息。因此,在C23的最新草案中,这一限制已经放宽,允许这样的使用,并且该修复也将被视为与之前的C版本相比的缺陷报告。因此,在未来,编译器应该再次允许您的代码,而不管您选择的是什么C++版本。
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()
,如下所示:这将允许代码使用GCC 11和更高版本进行编译。