Использование параметра шаблона в общей лямбда-выражении

GCC допускает следующий синтаксис в качестве расширения:

// a functional object that will add two like-type objects
auto add = [] <typename T> (T a, T b) { return a + b; };

В n3418 предложение 2012 г. по общие лямбда-выражения, мы видим синтаксис, который позволяет сделать следующее:

overload( []<class T>(T* p) {...},

Однако, поскольку это расширение, синтаксис явно отсутствует (или не разрешен). В каких ситуациях вышеизложенное было бы полезно, когда у нас есть auto, и почему синтаксис отсутствует (или не разрешен)?


person user4155483    schedule 18.10.2014    source источник


Ответы (1)


Мне кажется, что полиморфные лямбда-выражения C++14 просто более лаконичны.

Вы можете воспроизвести эффект вашего примера ситуации следующим образом:

struct A {};
struct B {};

int operator+(A, A) { return 1; }
int operator+(B, B) { return 2; }
int operator+(A, B) { return 3; }
int operator+(B, A) { return 4; }

int main() {
    auto add = [](auto a, decltype(a) b) { return a + b; };
    auto flexible_add = [](auto a, auto b) { return a + b; };

    add(A{}, A{});  // works
    add(B{}, B{});  // works
    add(A{}, B{});  // doesn't work

    flexible_add(A{}, A{});  // works
    flexible_add(B{}, B{});  // works
    flexible_add(A{}, B{});  // works

    auto deref = [](auto *a) { return *a; };
    int foo{};
    A a;
    B b;
    deref(&foo); // works
    deref(&a);   // works
    deref(&b);   // works
    deref(foo);  // doesn't work
    deref(a);    // doesn't work
    deref(b);    // doesn't work
}

Хотя есть много случаев, когда расширение GCC более функционально, не только в вашем случае использования (где оно подходит более естественно). Например, в отношении нетиповых параметров шаблона:

#include <cstddef>
#include <utility>
#include <iostream>

void print(std::initializer_list<std::size_t> il)
{
    for (auto&& elem : il) std::cout << elem << std::endl;
}

int main()
{
    auto indexed_lambda = [] <std::size_t... Is> (std::index_sequence<Is...>) { print({Is...}); };

    indexed_lambda(std::make_index_sequence<5>{});    
}

Coliru

Сложные общие типы параметров:

void foo() {}

int main() {
    auto accept_no_args_fun_only = [] <typename R> (R (*)()) {};

    accept_no_args_fun_only(foo);
}

Coliru

Вариады:

#include <tuple>
#include <vector>

int main() {
    auto accept_vector = [] (std::vector<auto> &&) {}; // Unconstrained placeholder from Concept TS, but not variadic
    auto accept_tuple = [] <typename... Args> (std::tuple<Args...> &&) {};

    accept_vector(std::vector{42});
    accept_tuple(std::tuple{42});
}

Coliru

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

РЕДАКТИРОВАТЬ

Расширение GCC было решено стать частью C++ на первое собрание стандартов C++20 ISO:

person pepper_chico    schedule 19.10.2014
comment
поэтому переведите этот код в свои более гибкие лямбды - person Piotr Skotnicki; 20.10.2014
comment
Я просто хотел указать, что есть случаи, когда универсальная лямбда не поддерживает - person Piotr Skotnicki; 20.10.2014
comment
Я не думаю, что ваш пример связан с вариативными шаблонами, а скорее с нетиповыми параметрами шаблона. - person Nir Friedman; 16.07.2017
comment
@NirFriedman Я просто не понимаю твоей точки зрения. нетиповые параметры шаблона и вариативные шаблоны не исключают друг друга, и в образце используются оба. Обратите внимание, что я не являюсь первоначальным автором финального образца, он был дословно скопирован из предложения Петра, которое я встроил, поскольку вижу актуальность. - person pepper_chico; 16.07.2017
comment
@pepper_chico Да, пример является одновременно вариативным и нетиповым, но синтаксис auto не имеет проблем с первым и не может выразить второй. Таким образом, утверждение, что gcc более эффективен в отношении вариативных параметров шаблона, неверно, и пример вводит в заблуждение. gcc более эффективен в отношении нетиповых параметров, вот и все, и пример с одним целочисленным параметром шаблона продемонстрирует это. - person Nir Friedman; 16.07.2017
comment
@NirFriedman Как бы вы выразили это с помощью общих лямбда-выражений С++ 14 coliru.stacked-crooked.com/ а/0d472d7a59d3cdfc? Я имею в виду, на прототипе. - person pepper_chico; 16.07.2017
comment
@pepper_chico Опять же, вы смешиваете что-то совершенно не связанное. Как бы вы сделали [] <typename Arg> (void (*)(Arg)) с общим лямбда-синтаксисом C++14? Проблема в вашем примере снова заключается не в вариативности, а в невозможности обработки шаблонов, которые появляются в выведенном контексте за пределами определенных конкретных случаев (в основном, квалификации cv, ref, pointer или массива). - person Nir Friedman; 16.07.2017
comment
@NirFriedman Хорошо, поэтому мы уже расширили возможности обработки просто нетипового параметра шаблона, чтобы включить и эти случаи. Согласитесь, опять же, не взаимоисключающие функции. О ваших заключительных замечаниях: в основном только это? Я вижу это как повсеместное, т.е. coliru.stacked-crooked.com/a/b1b4c33f1f0b024f. Обратите внимание, что на этот раз я поставил невариативную версию, кроме того, которая компилируется из-за Concepts TS, которая не распространяется на вариативную версию. Для последнего не требуется функция "Концепции", хотя она также охватывает вариативные шаблоны. Итак, на этом он более вариативен? - person pepper_chico; 16.07.2017
comment
@pepper_chico Конечно, в этом случае он более вариативен, потому что вы можете написать, например. tuple<auto>, но не tuple<auto...>, это понятно. Но это C++17 + Concepts. Первоначальный вопрос был о 14 (по какой-то причине вы добавили тег 20). В 14 tuple<auto> тоже нелегально. Итак, опять же, разница в возможностях совершенно не связана с вариадиками. Таким образом, утверждение о том, что gcc более эффективен в отношении вариативных параметров, остается неверным, а пример — вводящим в заблуждение. gcc более эффективен в отношении типов разрешенных вычетов. - person Nir Friedman; 16.07.2017
comment
@NirFriedman Давайте не будем путать возможности языковой функции со специфическими возможностями компилятора. Что касается языка, это было добавлено в С++ 20, разве это не ясно в редактировании ответа? Что делает информацию, обсуждаемую здесь, актуальной и для этого тега. Со стороны компилятора он был/является расширением GCC, но теперь установлен в качестве стандарта. Мой предыдущий вопрос, конечно, не ограничивался 14. - person pepper_chico; 16.07.2017
comment
@NirFriedman Я добавил окончательное редактирование, полностью переформулировав ответ, чтобы включить информацию и образцы в это обсуждение. Надеюсь, это больше не дезинформация. - person pepper_chico; 16.07.2017