Какой самый чистый способ передать имя типа в лямбду?

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

struct SpecialThing
{
    SpecialThing(/* some arguments */) {}
}

void f()
{
    template <typename Thing>
    auto createThing = [](auto&&... parameters)
    {
        return std::make_unique<Thing>(std::forward<decltype(parameters)>(parameters)...);
    };

    auto thing = createThing<SpecialThing>(/* some construction parameters */);
}

Но моему компилятору (GCC 4.9.2) это не нравится. (На самом деле я этого не ожидал, хотя я достаточно мало знаю о переменных шаблона, поэтому не мог быть уверен, что это не так.)

Предположим, я действительно решил сохранить createThing локальную переменную типа лямбда-функции. Насколько чисто я смогу таким образом обернуть std::make_unique? Вот лучшее, что у меня есть на данный момент:

void f()
{
    auto createThing = [](auto dummyThingPointer, auto&&... parameters)
    {
        typedef typename std::remove_pointer<decltype(dummyThingPointer)>::type Thing;
        return std::make_unique<Thing>(std::forward<decltype(parameters)>(parameters)...);
    };

    auto thing = createThing(static_cast<SpecialThing*>(nullptr), /* some construction parameters */);
}

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

Я думаю, что, возможно, я мог бы сделать что-то подобное с std::declval и std::remove_reference, но я не мог скомпилировать это. В любом случае это было бы не намного чище, чем указано выше.

Предоставляет ли C ++ 14 какой-нибудь незаметный способ превратить тип SpecialThing в createThing? Или, если это не удастся, хитрый способ лучше моего трюка с nullptr?

(Примечание: я знаю, что могу обойти это другими способами; я просто прошу изучить язык, а не преодолеть серьезное препятствие. Отсюда глупый код выше, который банально оборачивает стандартную функцию без видимой причины.)


person mjwach    schedule 17.05.2015    source источник


Ответы (1)


Я бы использовал тип тега:

#include <memory>
struct SpecialThing
{
    SpecialThing(/* some arguments */) {}
};
template<typename T>
struct Tag {
    typedef T type;
};
void f()
{
    auto createThing = [](auto typeTag, auto&&... parameters)
    {
        return std::make_unique<typename decltype(typeTag)::type>(
                        std::forward<decltype(parameters)>(parameters)...);
    };

    auto thing = createThing(Tag<SpecialThing>{} /*, some construction parameters */);
}
person Mankarse    schedule 17.05.2015
comment
Добавьте template<class Tag>using type_t=typename Tag::type;, чтобы сделать его немного чище, и using T=type_t<decltype(typeTag)>; в теле лямбды. Кроме того, для параметров auto&& decltype(parameters)(parameters) можно использовать для замены std::forward<decltype(parameters)>(parameters). - person Yakk - Adam Nevraumont; 17.05.2015
comment
@Yakk: Интересный момент о бесполезности std::forward. Когда нужно? - person Mankarse; 17.05.2015
comment
Я не думал об этом. Это устраняет уродливую уловку nullptr, а схема Yakk type_t, похоже, удаляет отсутствие ключевого слова typename из лямбда. Не тот чистый параметр шаблона, который мне нужен, но довольно близкий к нему! Я буду продолжать до тех пор, пока не появится лучший ответ (что, возможно, произойдет только в том случае, если язык изменится, я думаю). - person mjwach; 18.05.2015
comment
@Mankarse Ниже x находится переменная типа X, где X может быть ссылкой. T - это не справочный тип. Затем: std::forward<X>(x) отображает T->T&&, T&&->T&& и T&->T&. decltype(x)(x) отображает T->T, T&&->T&& и T&->T&. Есть отличия. Плюс forward что-то сообщает (имеется в виду в названии). Внутри автоматической лямбды, если у вас нет доступа к именам типов и вы используете auto&&, decltype(x)(x) делает вещи краткими. Я бы сам не стал использовать его где-либо еще, если бы мне не понадобилось T->T отображение. - person Yakk - Adam Nevraumont; 18.05.2015
comment
@Yakk: Спасибо за это. Тем не менее интересно отметить, что при обычном использовании std::forward, который, как правило, является вариацией template<typename... T> void f(T&&... t) {g(std::forward<T>(t)...);}, это не имеет значения (очевидно, std::forward имеет более широкое применение; покупайте ›90% случаев он просто применяется к T&& параметры). Поскольку параметры auto&& по сути являются такими, странно, что вы предпочли бы не использовать std::forward для auto&&..., но все равно использовали бы его для T&&.... Тем не менее, это то, о чем я раньше не думал. (: - person Mankarse; 18.05.2015
comment
Нет причин создавать свой собственный тип тега. Просто одолжите std::common_type<yourtypehere>. - person Deduplicator; 22.10.2015