Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Constexpr variable captured inside lambda loses its constexpr-ness

This code compiles fine in g++ (coliru), but not MSVC (godbolt and my VS2017).

#include <type_traits>
#include <iostream>
template<class T> void f(){
    constexpr bool b=std::is_same_v<T,int>; //#1
    auto func_x=[&](){
        if constexpr(b){ //#error
        }else{
        }
    };
    func_x();
}
int main(){
    f<int>();
}

(6): error C2131: expression did not evaluate to a constant
(6): note: failure was caused by a read of a variable outside its lifetime
(6): note: see usage of 'this'

Which one (g++ or MSVC) is wrong?
What is this in "see usage of 'this'"??

How to work around it while keep the compile-time guarantee?

In my real case, b (#1) is a complex statement depends on a few other constexpr variables.

like image 964
javaLover Avatar asked Mar 13 '19 07:03

javaLover


People also ask

Can Lambda be constexpr?

Visual Studio 2017 version 15.3 and later (available in /std:c++17 mode and later): A lambda expression may be declared as constexpr or used in a constant expression when the initialization of each data member that it captures or introduces is allowed within a constant expression.

Do constexpr variables take memory?

The alternatives don't have the all of the positives of static constexpr - you're guaranteed compile time processing, type safety, and (potentially) lower usage of memory (constexpr variables don't need to take up memory, they are effectively hard coded unless if possible).

Does constexpr need to be static?

A static constexpr variable has to be set at compilation, because its lifetime is the the whole program. Without the static keyword, the compiler isn't bound to set the value at compilation, and could decide to set it later. So, what does constexpr mean?

What is a constexpr variable?

constexpr indicates that the value, or return value, is constant and, where possible, is computed at compile time. A constexpr integral value can be used wherever a const integer is required, such as in template arguments and array declarations.


2 Answers

Gcc is right. b (as constexpr variable) doesn't need to be captured in fact.

A lambda expression can read the value of a variable without capturing it if the variable

  • is constexpr and has no mutable members.

GCC LIVE

It seems if making b static then MSVC could access b without capturing.

template<class T> void f(){
    constexpr static bool b=std::is_same_v<T,int>;
    auto func_x=[](){
        if constexpr(b){
        }else{
        }
    };
    func_x();
}

MSVC LIVE

And

How to work around it while keep the compile-time guarantee?

We can't keep the constexpr-ness for the captured variables. They become non-static data members of the lambda closure type and non-static data members can't be constexpr.

like image 119
songyuanyao Avatar answered Sep 22 '22 09:09

songyuanyao


How to work around it while keep the compile-time guarantee?

Marking the constexpr bool as static serves as a work around.

See Demo

Alternately, you can use the condition in the if constexpr instead of assigning it to a bool. Like below:

if constexpr(std::is_same_v<T,int>)

See Demo

Note that there have been bugs raised for MSVC regarding constexpr with respect to lambda expressions.
One such is: problems with capturing constexpr in lambda
and another is: if constexpr in lambda

like image 30
P.W Avatar answered Sep 20 '22 09:09

P.W