GCC и Clang расходятся во мнениях по поводу совокупности лямбды? - PullRequest
3 голосов
/ 11 июня 2019

Почему Clang не может скомпилировать следующий код с сообщением, что выражение не является constexpr, и почему GCC нет?Какой компилятор правильный?https://godbolt.org/z/nUhszh (Очевидно, это всего лишь пример. Мне действительно нужно иметь возможность вызывать объект-функцию constexpr в контексте constexpr.)

#include <type_traits>

template <typename Predicate>
constexpr int f(Predicate&& pred) {
    if constexpr (pred(true)) {
        return 1;
    } 
    else {
        return 0;
    }
}

int main() {
    f([](auto m) {
        return std::is_same_v<decltype(m), bool>;
    });
}

Вывод clang8.0.0 с -std=c++17 -stdlib=libc++ -O1 -march=skylake:

<source>:5:19: error: constexpr if condition is not a constant expression

    if constexpr (pred(true)) {

                  ^

<source>:14:5: note: in instantiation of function template specialization 'f<(lambda at <source>:14:7)>' requested here

    f([](auto m) {

    ^

1 error generated.

Compiler returned: 1

Ответы [ 2 ]

3 голосов
/ 11 июня 2019

Используя предикат в if constexpr, вы ожидаете, что он безусловно будет константным выражением.Но функция constexpr может всегда вызываться в контексте non-constexpr с аргументами, которые не являются константными выражениями.Таким образом, параметр функции может никогда не считаться константным выражением.

Поэтому GCC неправильно принимать ваш код без изменений.

Просто так получилось, что ваш конкретный примерне требует if constexpr для работы в контексте constexpr.Измененная функция:

template <typename Predicate>
constexpr int f(Predicate&& pred) {
    if (pred(true)) {
        return 1;
    } 
    else {
        return 0;
    }
}

Вызывается обоими компиляторами , когда требуется постоянное выражение:

int main() {
    constexpr int i = f([](auto m) constexpr {
        return std::is_same_v<decltype(m), bool>;
    });
    return i;
}
2 голосов
/ 11 июня 2019

Clang прав.В выражении pred(true), id-выражение pred обозначает переменную ссылочного типа.Переменная ссылочного типа может появляться в константном выражении, только если они инициализируются константным выражением или если их инициализация выполняется во время вычисления выражения ( [expr.const] /2.11).

То есть pred(true) не является константным выражением.

Если вы измените объявление параметра pred на Predicate pred, Pred не будет ссылочного типа.Выражение pred(true) будет эквивалентно pred.operator()(true).pred будет объектным выражением в выражении доступа к члену класса , и поэтому преобразование lvalue-to-rvalue не будет применяться к pred.Таким образом, id-выражение pred не нужно будет инициализировать константным выражением (см. [expr.const] /2.7.2) в порядкебыть константным выражением .Тогда вызов функции является константным выражением , поскольку оператор вызова неявно является функцией constexpr [expr.prim.lambda.closure] / 4 .

Эти факты оправдываютпредложение @NikosC.объявить Pred как Predicate Pred.В этом случае ваш код будет компилироваться как с Clang, так и с Gcc и будет соответствовать стандарту.

...