C ++ 20: Лямбда без захвата в нетипичном параметре шаблона - PullRequest
5 голосов
/ 16 января 2020

Позволяет ли C ++ 20 не захватывать лямбда-выражения, распадающиеся на указатель функции, для прямой передачи в качестве нетипового параметра шаблона? Если да, какой правильный синтаксис?

Я пробовал следующий код в различных версиях clang и g cc, используя -std=c++2a.

#include <iostream>

template<auto f>
struct S {
    static void invoke(int x) { f(x); }
};

using X = S<+[](int x) -> void { std::cout << x << " hello\n"; }>;

int main()
{
    X::invoke(42);
}

g cc компиляции код без жалоб и код работает как ожидалось.

clang не может скомпилировать со следующей ошибкой:

error: a lambda expression cannot appear in this context
using X = S<+[](int x) -> void { std::cout << x << " hello\n"; }>;
             ^

Вот полный код (онлайн-версии):

Clang 10.0.0 HEAD: https://wandbox.org/permlink/n5eKQ4kQqSpDpr4k

G cc 10.0.0 HEAD 20200113: https://wandbox.org/permlink/vJ44sdMtwCKAFU64

Ответы [ 3 ]

8 голосов
/ 16 января 2020

Позволяет ли C ++ 20 не захватывать лямбду, распадающуюся на указатель функции, для передачи непосредственно в качестве нетипового параметра шаблона?

Да.

Действительно, вы можете go сделать еще один шаг - вам даже не нужно преобразовывать лямбду в указатель на функцию. Вы можете просто предоставить лямбду. Это действительно C ++ 20:

using Y = S<[](int x) -> void { std::cout << x << " hello\n"; }>;

Правило, которое мы имеем в C ++ 20, заключается в том, что лямбды теперь разрешены в неоцененных контекстах ( P0315 ). Среди многих других изменений формулировок в этой статье нарушено правило, запрещающее использование лямбда-выражений в аргументах шаблона ( C ++ 17 [expr.prim.lambda] / 2 ):

A лямбда-выражение не должно появляться в неоцененном операнде, в аргументе шаблона , в объявлении псевдонима , в объявлении typedef или в объявлении функции или шаблона функции вне тела функции и аргументов по умолчанию.

Это предложение больше не существует в C ++ 20.

Снятие этого ограничения позволяет использовать лямбду в качестве аргумента шаблона, а преобразование из лямбды без захвата в указатель на функцию уже было constexpr в C ++ 17. Clang просто не реализует эту функцию (using T = decltype([]{}); компилируется на g cc, еще не на Clang). Я бы еще не назвал эту ошибку лягушкой, это всего лишь функция лягушки, которая еще не реализована (лямбда-выражения в неоцененных контекстах еще не перечислены как реализованные на странице поддержки компилятора cppreference ).

C ++ 20 нетипизированных параметров шаблона ( P1907 ) позволяет даже отбрасывать +, потому что лямбды без захвата считаются структурными типами ( [temp.param] / 7 ) просто из-за отсутствия элементов данных.

0 голосов
/ 16 января 2020

Если после этого правила C ++ 17 не изменились, то использование лямбды в качестве параметра шаблона не допускается по той же причине, по которой использование строкового литерала не допускается. Каждая лямбда имеет свой тип, и каждый строковый литерал ссылается на свой объект. Что изменилось в C ++ 17, так это то, что объекты замыкания теперь constexpr. Чтобы использовать строковый литерал или лямбду в качестве параметра шаблона, объект должен иметь внешнюю связь. Так что это разрешено в C ++ 17.

template <auto>
struct S {};

constexpr const char string[] = "String literal";
constexpr auto lambda = []{};

S<string> a;
S<+lambda> b;

Сам объект замыкания не может использоваться как параметр шаблона (поэтому вы не можете сделать S<lambda>), но это могло измениться в C ++ 20 с трехсторонним сравнением. Причина, по которой объекты должны иметь внешнюю связь, заключается в том, что они как бы разбивают шаблоны. S<+[]{}> и S<+[]{}> будут считаться разными типами, даже если они выглядят одинаково (аналогично S<"">).

0 голосов
/ 16 января 2020

Параметр шаблона должен быть переменной constexpr.

Есть соответствующее предложение N4487 для лямбд.

Я не знаю, было ли оно сделано в C ++ 20.

...