Этот код нормально компилируется в gcc, но не работает в Visual C++.
MCVE = https://godbolt.org/z/K7d5PEs65
int main(){
int localVariable=0; //some local variable
auto lamb=[&]<int s>() {
if constexpr(s==5){localVariable=7;}
///^ may modify some local variables
return 8;
};
constexpr int gOk=lamb.operator()<2>();
[&]<int s2>() {
///vvv can edit only here
constexpr int gFail=lamb.operator()<s2>();
///^^^ can edit only here
}.operator()<2>();
}
Выражение ошибки C2131 не получило константу
Мне нужно, чтобы gFail
была переменной constexpr
, а lamb
иногда создавало побочный эффект во время выполнения, в зависимости от значения переменной шаблона s
.
Пожалуйста, дайте ссылку и обходной путь для случая Visual C++.
Обновлено: Кстати, причина, по которой я смешиваю constexpr и не-constexpr внутри одного и того же lamb
, заключается в том, чтобы сделать вызов удобным. По задумке пользователя, он должен заранее знать, является ли экземпляр функции lamb
constexpr или нет.
Даже не нужно вызывать вторую лямбду. И захват lamb
по значению ничему не помогает. В ошибке говорится, что «сбой был вызван чтением переменной за пределами ее срока службы», но я не понимаю, о какой переменной может идти речь. Для меня это похоже на ошибку.
Ах. Само создание lamb
constexpr
помогает на упрощенном примере; но не оригинал: «сбой был вызван взятием адреса объекта, не имеющего статического срока хранения». Похоже, здесь нет никакого способа победить: если lamb
есть constexp
, он не может изменять локальную переменную, а если это не constexpr
, его нельзя использовать для инициализации переменной constexpr
.
Мне пришлось бороться с той же проблемой «чтения переменной за пределами ее жизни» в MSVC. Я думаю это ошибка/недостаток компилятора.
Я считаю, что проблема, с которой вы столкнулись, связана с ошибкой/недостатком MSVC, но я бы позволил языковым юристам указать точные причины (или опровержение моего утверждения).
Обычно в таких ситуациях я упаковываю значение constexpr
в целочисленную константу. Это то, что MSVC может пережевать:
#include <iostream>
#include <cstdlib>
int main(){
int localVariable=0; //some local variable
auto lamb=[&]<int s>() {
if constexpr(s==5){localVariable=7;}
return std::integral_constant<int, 8>{};
};
constexpr int gOk=lamb.operator()<2>().value;
[&]<int s2>() {
auto tmp = lamb.operator()<s2>();
constexpr int gFail=tmp; //tmp implicitly casted from integral_constant to int.
}.operator()<2>();
}
tmp
необходимо явно указать для MSVC. Если вы назначите непосредственно gFail
, вы получите то же самое «не оценивать константу».
К сожалению, это не соответствует вашему руководству «можно редактировать только здесь».
Обновление: я обеспокоен тем, что то, что вы делаете, на самом деле не будет работать ни в одном компиляторе. Если вы попытаетесь использовать 5
в качестве аргумента шаблона, вы действительно получите ошибку во всех трех компиляторах:
#include <iostream>
#include <cstdlib>
int main(){
int localVariable=0; //some local variable
auto lamb=[&]<int s>() {
if constexpr(s==5){localVariable=7;} //error
///^ may modify some local variables
return 8;
};
constexpr int gOk=lamb.operator()<5>();
std::cout << localVariable;
}
gcc дает:
<source>: In function 'int main()':
<source>:12:43: in 'constexpr' expansion of 'lamb.main()::<lambda()>()'
<source>:8:43: error: the value of 'lamb' is not usable in a constant expression
8 | if constexpr(s==5){localVariable=7;}
| ~~~~~~~~~~~~~^~
<source>:7:12: note: 'lamb' was not declared 'constexpr'
7 | auto lamb=[&]<int s>() {
| ^~~~
Compiler returned: 1
Спасибо за первую часть. Это интересное открытие. Это может быть полезно или очень полезно. ....... Вторая часть :: По моему замыслу, вторая часть с <5>
(таким образом, изменяя переменную во время выполнения) должна завершиться неудачей. Все в порядке.
Откуда вы узнали этот правильный ответ? Кажется, так сложно угадать.
Как я уже говорил в другом комментарии, я боролся с MSVC из-за чего-то подобного с тем же сообщением об ошибке.
Можно даже упростить Демо