I have this example code
#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;
}
It compiles fine in [OnlineGDB][1]
. And in the older versions of gcc
. However, it started failing in 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{};
| ~~~~~~~~~^~
The code is the same. The language version is the same. I know how to fix this error. I just would like to know what was changed in the compiler? What is the reason for this change and thus the failure?
The code has not been valid until recently. If the compiler allowed it before without diagnostic, then the compiler wasn't conforming to the standard at the time.
The issue is simply that
data
infunction
is a reference. If it was taken by-value instead, then it would be fine.One of the rules that disqualify an expression like
data.size()
from being a constant expression, as required for template arguments, is that any reference named in the expression (and whose lifetime didn't start during the evaluation of the constant expression) is itself initialized with a constant expression, even if no lvalue-to-rvalue conversion is applied to the referenced object and the identity of the referenced object is irrelevant. A function parameter isn't initialized with a constant expression and sodata
cannot be used in a constant expression inside the function in any capacity.There isn't really any good reason for this restriction to be that strict. You only want to call
.size()
which just returns a constant determined from the type. It doesn't need any information about the actual object on which it is called.So the restriction has been softened in the latest draft for C++23 to allow for such use and the fix will also be considered a defect report against previous C++ revisions. So in the future the compilers should again allow your code regardless of the chosen C++ version.