Есть ли элегантный способ написать псевдоним типа в полиморфной лямбде - PullRequest
0 голосов
/ 02 мая 2018

Рассмотрим следующий код:

#include <initializer_list>
#include <vector>

    auto cref_lambda = [] (const auto& il){
        using T= typename decltype(il)::value_type;   
    };

    auto cval_lambda = [] (const auto il){
        using T=typename decltype(il)::value_type;
    };

    int main(){
    std::initializer_list<int> il;
    cref_lambda(il);
    cval_lambda(il);
    }

cref_lambda не компилируется, потому что мы пытаемся :: в ссылку.

Я знаю об обходных путях (используя std::remove_reference_t или просто используя decltype(*il.begin());), но мне интересно, есть ли лучшая идиома для использования здесь.

1 Ответ

0 голосов
/ 02 мая 2018

Способ решения вашей проблемы - добавить std::decay_t к инструкции decltype. От cppreference :

Применяет неявные преобразования lvalue-to-rvalue, array-to-pointer и function-to-pointer к типу T, удаляет cv-квалификаторы и определяет результирующий тип как тип typedef члена.

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

using T = typename std::decay_t<decltype(il)>::value_type;   

для получения неквалифицированного value_type, независимого от сигнатуры функции.


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

template < typename T >
void cref(std::initializer_list<T> const &il) {
    /* use T and il */
}

или если он должен работать для любого контейнера

template < typename U >
void cref(U const &il) {
    using T = typename U::value_type;
    /* use T and il */
}

Очевидным преимуществом первого случая является то, что вы получаете доступ к T = value_type «бесплатно». Другое преимущество (на мой взгляд) состоит в том, что вы получите гораздо более ясную ошибку компилятора, если случайно вызовете эту функцию с чем-то, что не является std::initializer_list<T>. Вы можете исправить этот недостаток лямбды, добавив static_assert, но это еще больше усугубит «недостаток», который вы изначально хотели найти.


Наконец, если вам действительно нравится лямбда-стиль написания функций или вам нужно что-то захватывать и вы не можете использовать подход свободных функций, вы можете рассмотреть возможность использования расширения GCC для лямбда-шаблонов:

auto cref_lambda = [] <typename U> (U const &il){
    using T = typename U::value_type;
};

Это, наверное, самое короткое, что вы можете получить.

...