【问题标题】:What's the cleanest way to pass a typename into a lambda?将类型名传递给 lambda 的最干净的方法是什么?
【发布时间】:2015-05-17 06:11:06
【问题描述】:

我有兴趣编写一个 lambda 函数,该函数将调用 std::make_unique。要调用std::make_unique,我需要一个类型名,但似乎为了将类型名直接传递给 lambda 函数,我必须将 lambda 变量设为模板:

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 保留为 lambda 函数类型的局部变量。我能以这种方式将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::declvalstd::remove_reference 做类似的事情,但我无法编译。反正不会比上面的干净多少。

C++14 是否提供任何非偷偷摸摸的方式来将 SpecialThing 类型转换为 createThing 这里?或者失败了,一个比我的 nullptr 技巧更好的偷偷摸摸的方法?

(注意:我知道我可以通过其他方式解决这个问题;我只是要求学习该语言,而不是为了克服严重的障碍。因此,上面的愚蠢代码简单地包装了一个标准函数,没有明显原因。)

【问题讨论】:

    标签: c++ templates lambda c++14


    【解决方案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 */);
    }
    

    【讨论】:

    • 添加 template&lt;class Tag&gt;using type_t=typename Tag::type; 使其更简洁,并在 lambda 主体中添加 using T=type_t&lt;decltype(typeTag)&gt;;。另外,对于auto&amp;&amp;参数,可以用decltype(parameters)(parameters)代替std::forward&lt;decltype(parameters)&gt;(parameters)
    • @Yakk:关于std::forward 无用的有趣点。什么时候需要
    • 我没想到。这消除了丑陋的 nullptr 诡计,并且 Yakk 的 type_t 方案似乎从 lambda 中删除了“typename”关键字的实例。不是我想要的纯模板参数,但非常接近它!我会继续这样做,直到出现更好的答案(我猜这可能只有在语言发生变化时才会发生)。
    • @Mankarse x 下面是X 类型的变量,其中X 可以是一个引用。 T 是非引用类型。然后:std::forward&lt;X&gt;(x) 映射 T-&gt;T&amp;&amp;T&amp;&amp;-&gt;T&amp;&amp;T&amp;-&gt;T&amp;decltype(x)(x) 映射 T-&gt;TT&amp;&amp;-&gt;T&amp;&amp;T&amp;-&gt;T&amp;。有区别。加上forward 传达了一些东西(名字中的意思)。在自动 lambda 中,如果您无法访问类型的名称,并且您使用的是 auto&amp;&amp;decltype(x)(x) 会保持简短。我自己不会在其他地方使用它,除非我想要 T-&gt;T 映射。
    • @Yakk:谢谢。值得注意的是,在std::forward 的常见用法中,它往往是template&lt;typename... T&gt; void f(T&amp;&amp;... t) {g(std::forward&lt;T&gt;(t)...);} 的变体,它并没有什么不同(显然std::forward 具有更广泛的适用性;购买>90% 的时间它只是应用于T&amp;&amp; 参数)。由于auto&amp;&amp; 参数本质上是这样,奇怪的是您宁愿不将std::forward 用于auto&amp;&amp;...,但仍将其用于T&amp;&amp;...。不过,这是我以前没有考虑过的。 (:
    猜你喜欢
    • 2019-11-28
    • 1970-01-01
    • 1970-01-01
    • 2021-08-31
    • 2020-05-29
    • 1970-01-01
    • 1970-01-01
    • 2019-12-19
    • 2014-05-03
    相关资源
    最近更新 更多