Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
694 views
in Technique[技术] by (71.8m points)

c++ - Why a std::array is not constant expression when it is the input of a templated function/generic lambda?

(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 << '
';
};

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 wonder 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.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

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 << '
';
};

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 << '
';
};

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{}); }

Addendum: this may change in future, per the paper P2280R1.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

2.1m questions

2.1m answers

60 comments

57.0k users

...