22

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.

HolyBlackCat
  • 78,603
  • 9
  • 131
  • 207
javaLover
  • 6,347
  • 2
  • 22
  • 67

2 Answers2

18

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.

songyuanyao
  • 169,198
  • 16
  • 310
  • 405
  • Is there a location in the C++ standard where this is stated? – Nicol Bolas Mar 13 '19 at 14:52
  • Indeed, C++17 seems to [directly contradict this](https://timsong-cpp.github.io/cppwp/n4659/expr.prim.lambda.capture#7). `b` is implicitly captured by the lambda; there is no caveat about being a constant expression. – Nicol Bolas Mar 13 '19 at 14:59
  • 3
    @NicolBolas since `b` is `constexpr`, performing an lvalue-to-rvalue conversion on it directly does not constitute an odr-use. See [basic.def.odr]/3 for the standard reference. – Brian Bi Mar 13 '19 at 15:14
10

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

P.W
  • 26,289
  • 6
  • 39
  • 76
  • The bugs are closed as "fixed", but the fact is that the bugs are still there as of version 17.3.5 (Visual Studio Professional 2022)... – Jakub Klinkovský Oct 28 '22 at 13:34