问题
(Realted to this other question of mine; if you give a look at that too, I would really appreciate it.)
If std::array<T,N>::size is constexpr, then why does the following code not even compile?
#include <array>
#include <iostream>
constexpr auto print_size = [](auto const& array){
constexpr auto size = array.size();
std::cout << size << '\n';
};
int main() {
print_size(std::array<int,3>{{1,2,3}});
}
The error is the following:
$ g++ -std=c++17 deleteme.cpp && ./a.out
deleteme.cpp: In instantiation of ‘<lambda(const auto:1&)> [with auto:1 = std::array<int, 3>]’:
deleteme.cpp:10:42: required from here
deleteme.cpp:5:20: error: ‘array’ is not a constant expression
5 | constexpr auto size = array.size();
| ^~~~
But I wander why.
At the lambda call site, the argument is known at compile time, and the lambda should be instantiated with auto
equal to std::array<int,3>
, where 3
is a compile time value, and so should be output of array.size()
.
What is wrong in my reasoning?
By the way, the same holds if I use a templated function instead of the generic lambda.
回答1:
The problem is [expr.const]/5.12:
5 - An expression E is a core constant expression unless the evaluation of E, following the rules of the abstract machine ([intro.execution]), would evaluate one of the following: [...]
- (5.12) an id-expression that refers to a variable or data member of reference type unless the reference has a preceding initialization and either
- (5.12.1) it is usable in constant expressions or
- (5.12.2) its lifetime began within the evaluation of E;
Since the variable array
is a reference, it is not permitted to evaluate it (inside the expression array.size()
), even though the evaluation doesn't actually do anything.
Passing array
by value (const
or non-const
) makes the code valid:
constexpr auto print_size = [](auto const array){
constexpr auto size = array.size(); // ok
std::cout << size << '\n';
};
But taking a reference to that parameter and using it on the very next line is invalid:
constexpr auto print_size = [](auto const arr){
auto const& array = arr;
constexpr auto size = array.size(); // error
std::cout << size << '\n';
};
Note that gcc 9 incorrectly accepts this code; it is only since version 10 that gcc gets this correct.
gcc 10 still is noncompliant in a related area; it accepts calling a static constexpr
member function on a reference. Using a constexpr static member of a reference as template argument This is incorrect and clang correctly rejects it:
struct S { static constexpr int g() { return 1; } };
void f(auto const& s) {
constexpr auto x = s.g(); // error
constexpr auto y = decltype(s)::g(); // ok
}
int main() { f(S{}); }
来源:https://stackoverflow.com/questions/65236583/why-a-stdarray-is-not-constant-expression-when-it-is-the-input-of-a-templated