якщо constexpr із static_assert в лямбда, який компілятор є правильним?


13

Коли ми хочемо використовувати a static_assertв a, if constexprми повинні зробити умову залежною від певного параметра шаблону. Цікаво, що gcc та clang не погоджуються, коли код загорнутий у лямбда.

Наступний код компілюється з gcc, але clang викликає ствердження, навіть якщо це if constexprне може бути правдою.

#include <utility>

template<typename T> constexpr std::false_type False;

template<typename T>
void foo() {

    auto f = [](auto x) {
        constexpr int val = decltype(x)::value;
        if constexpr(val < 0) {
            static_assert(False<T>, "AAA");
        }
    };

    f(std::integral_constant<int, 1>{});
}

int main() {
    foo<int>();
}

Живий приклад тут .

Це можна легко виправити, замінивши False<T>на False<decltype(x)>.

Тож питання: який компілятор правильний? Я б припустив, що gcc є правильним, тому що від умови static_assertзалежить від T, але я не впевнений.


Це задає щось таке ж питання, але йде з протилежного напрямку: stackoverflow.com/questions/59393908/… .
НатанОлівер

1
@mfnx Неможливо відтворити . Чи можете ви поділитися прикладом?
НатанОлівер

2
Я б сказав, що обидва мають рацію (погано сформована НДР): static_assert(False<int>, "AAA");еквівалентна static_assert(false, "AAA");всередині лямбда.
Jarod42

2
@mfnx Ви змінили значення константи. Використання прикладу ОП, де константа f(std::integral_constant<int, 1>{});Wandbox, не викликає твердження: wandbox.org/permlink/UFYAmYwtt1ptsndr
NathanOliver

1
@NathanOliver Так, ви праві, вибачте за шум. Здається, що gcc є правильним, оскільки цей код у constexpr слід відкинути, якщо константа> = 0;
mfnx

Відповіді:


1

З [stmt.if] / 2 (наголос мій)

Якщо оператор if має вигляд, якщо constexpr, значення умови має бути контекстуально перетвореним постійним виразом типу bool; ця форма називається constexpr if оператором. Якщо значення перетвореної умови є хибним, перший підзаголовок - це відкинутий вислів, інакше другий підзаголовок, якщо він присутній, є відхиленим твердженням. Під час створення екземпляра об'єкта, що додається до шаблону ([temp.pre]), якщо умова після його інстанціювання не залежить від значення, відкинута підстава (якщо така є) не створюється.

Читаючи, що можна подумати, що статичне твердження буде відхилено, але це не так.

Статичне затвердження запускається на першій фазі шаблону, оскільки компілятор знає, що це завжди помилково.

Із [temp.res] / 8 (наголос мій)

Дійсність шаблону може бути перевірена до будь-якої інстанції. [ Примітка. Знання того, які імена є іменами типів, дозволяє перевірити синтаксис кожного шаблону таким чином. - кінцева примітка ] Програма неправильно сформована, не потрібна діагностика, якщо:

  • (8.1) жодна дійсна спеціалізація не може бути сформована для шаблону або підзадачі конспектора, якщо заява в межах шаблону і шаблону не є екземпляром , або

[...]

Так, від вашого False<T>залежить T. Проблема полягає в тому, що загальна лямбда - це сам шаблон і False<T>не залежить від будь-якого параметра шаблону лямбда.

Для того, Tщо False<T>є хибним, статичне твердження завжди буде помилковим, незалежно від того, який аргумент шаблону надсилається лямбда.

Компілятор може бачити, що для будь-якої інстанції шаблону operator()статичне затвердження завжди буде тригером для поточного Т. Отже, помилка компілятора.

Рішення для цього може залежати від x:

template<typename T>
void foo() {

    auto f = [](auto x) {
        if constexpr(x < 0) {
            static_assert(False<decltype(x)>, "AAA");
        }
    };

    f(std::integral_constant<int, 1>{});
}

Живий приклад


13

Звичайне правило тут [temp.res] / 8 :

Програма неправильно сформована, не потрібна діагностика, якщо: не може бути сформована дійсна спеціалізація для шаблону або підзадачі constexpr, якщо заява в межах шаблону і шаблон не є ініційованим

Після того, як ви придумали foo<T>, static_assertви вже не залежите. Це стаєstatic_assert(false) - на всі можливі моменти оператора виклику родової лямбда f. Це неправильно сформовано, діагностики не потрібно. Кланг діагностує, gcc не робить. Обидва вірні.

Зверніть увагу , що це не має значення , що static_assertтут буде відкинуто.

Це можна легко виправити, замінивши False<T>на False<decltype(x)>.

Це утримує static_assertзалежних від загальної лямбда, і тепер ми потрапляємо в стан, коли гіпотетично могла б бути справжня спеціалізація, тому ми більше не формуємось, ndr.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.