【问题标题】:Is it safe to assume that identical lambda expressions have different types?假设相同的 lambda 表达式具有不同的类型是否安全?
【发布时间】:2018-05-18 19:04:32
【问题描述】:

我正在试验 lambdas 以及不同的 lambda 表达式具有不同类型的事实,即使它们是相同的。考虑这段代码

#include <iostream>

template <typename T> void once(T t){
    static bool first_call = true;
    if (first_call) t();
    first_call = false;
}

int main() {    
    int counter = 0;
    auto a = [&counter](){counter++;};
    once(a);
    once(a);
    std::cout << counter;              // 1

    auto b = a;                        // same type
    once(b);
    std::cout << counter;              // 1

    auto c = [&counter](){counter++;}; // different type
    once(c);
    once(c);               
    std::cout << counter;              // 2
}

这打印出112,即ab 当然是同一种类型而c 有不同的类型。

是否允许编译器让ca 属于同一类型?

我的意思是表达式是相同的,这将是一个明显的优化。

PS:如果捕获阻止了这种优化,那么没有捕获的 lambdas 呢?

相关:what is the type signature of a c++11/1y lambda function?Can the 'type' of a lambda expression be expressed?

【问题讨论】:

    标签: c++ optimization lambda language-lawyer


    【解决方案1】:

    是否允许编译器让ca 属于同一类型?

    没有。 [&amp;counter](){counter++;} 是一个 lambda 表达式,根据 [expr.prim.lambda.closure]/1

    lambda 表达式的类型(也是闭包对象的类型)是唯一的、未命名的非联合类类型,称为闭包类型,其属性如下所述。

    因此,对于每个 lambda 表达式,即使它与前一个相同,您也会得到一个唯一的类型。

    您可以使用typeid 来检查是否是这种情况,例如:

    #include <iostream>
    #include <typeinfo>
    
    template <typename T> void once(T t){
        static bool first_call = true;
        std::cout << typeid(t).name() << std::endl;
        if (first_call) {
            t();
        }
        first_call = false;
    }
    
    int main() {    
        int counter = 0;
        auto a = [&counter](){counter++;};
        once(a);
        once(a);
        std::cout << counter << std::endl; // 1
    
        auto b = a;                        // same type
        once(b);
        std::cout << counter << std::endl; // 1
    
        auto c = [&counter](){counter++;}; // different type
        once(c);
        once(c);               
        std::cout << counter << std::endl; // 2
    }
    

    结果:

    Z4mainEUlvE_                                                                                                          
    Z4mainEUlvE_                                                                                                          
    1                                                                                                                     
    Z4mainEUlvE_                                                                                                          
    1                                                                                                                     
    Z4mainEUlvE0_                                                                                                         
    Z4mainEUlvE0_                                                                                                         
    2
    

    你可以看到有两个函数模板实例化。

    【讨论】:

    • 我不得不承认我的标准语真的很糟糕。是不是一般情况下每个表达式都被认为是唯一的,即使它说的是同一件事?
    • @user463035818 好吧,a += 10; 将始终具有相同的类型,无论您在哪里调用它。 lambda 是一种特殊类型的表达式,因为它们只是编写函子的语法糖。所以,我不会说每个表达式都是唯一的,但可以保证每个 lambda 表达式的类型都是唯一的。
    • @user463035818 请注意,此规则是您不能将任何 lambda 传递给非模板函数的原因:您不能写下它的类型来声明参数。反过来,这种无法传递 lambda 是 std::function&lt;&gt; 存在的原因:它类型擦除唯一的 lambda 类型,并为您提供基于 lambda 签名的类型,您可以实际写下。
    • 是的 a += 10; 总是具有相同的类型,我指的是 a += 10; a += 10; 是两个不同(唯一)的表达式。我以前从未考虑过这一点。如果在我的代码中[&amp;counter](){counter++;}; 的两个出现被认为是一个相同的表达式,那么您引用的部分将允许它们具有相同的类型,但它们不会
    猜你喜欢
    • 2021-05-08
    • 2021-09-09
    • 1970-01-01
    • 2023-03-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-18
    相关资源
    最近更新 更多