【问题标题】:Can the 'type' of a lambda expression be expressed?可以表达 lambda 表达式的“类型”吗?
【发布时间】:2011-04-21 12:05:15
【问题描述】:

将 lambda 表达式视为可调用对象的“语法糖”,可以表达未命名的底层类型吗?

一个例子:

struct gt {
    bool operator() (int l, int r) {
        return l > r;
    }
} ;

现在,[](int l, int r) { return l > r; } 是上述代码的优雅替代品(加上必要的 gt 可调用对象的创建),但有没有办法表达 gt(类型)本身?

一个简单的用法:

std::set<int, gt> s1;  // A reversed-order std::set
// Is there a way to do the same using a lambda?
std::set<int, some-magic-here-maybe([](int l, int r) { return l > r; }) > s2;

【问题讨论】:

  • 这是我不喜欢 lambda 表达式的原因之一。假设您在代码的 20 个区域中需要相同的比较器。你会写 20 次比较器还是写一次并作为函子/函数指针传递?
  • @Donotalo:所以你是说因为在某些情况下它不是最好的解决方案,所以它永远不会成为一个好的解决方案?使用 lambdas 的原因是 oyu 通常只需要在 一个 位置使用该比较器。如果您需要它 20 次,那么我认为 任何人 都不会建议将其表示为 lambda。
  • @Donotalo:lambdas 通常很简单,可以在程序的不同位置复制粘贴;代码重用对于非平凡的函数很有用,但你不应该为它们使用 lambda
  • 好的,明白了。实际上我从来没有在一个地方需要像 lambda 这样的东西。通常我为需要排序的类定义operator&lt;()。这就是为什么我不明白为什么需要 lambda。
  • @Donotalo:不仅如此,您还可以通过使用 std::function 和适当的参数集,将 lambda 表达式绑定到变量。

标签: c++ stl lambda c++11


【解决方案1】:

不,您不能将其放入decltype,因为

lambda 表达式不得出现在未计算的操作数中

您可以执行以下操作

auto n = [](int l, int r) { return l > r; };
std::set<int, decltype(n)> s(n);

但这真的很难看。请注意,每个 lambda 表达式都会创建一个新的唯一类型。如果之后您在其他地方执行以下操作,t 的类型与 s 不同

auto n = [](int l, int r) { return l > r; };
std::set<int, decltype(n)> t(n);

您可以在此处使用std::function,但请注意,这会产生一点运行时成本,因为它需要间接调用 lambda 函数对象调用运算符。它在这里可能可以忽略不计,但如果你想以这种方式将函数对象传递给std::sort,则可能很重要。

std::set<int, function<bool(int, int)>> s([](int l, int r) { return l > r; });

与往常一样,先编写代码,然后配置文件 :)

【讨论】:

  • 当我阅读问题时,我想到了将autodeclytype 组合使用。 chuckle 使用这种组合来提取实际上并不意味着具有可见类型的表达式的类型有点有趣。
  • “lambda 表达式不应出现在未计算的操作数中”似乎很愚蠢,没有经过深思熟虑。它有什么用,你知道吗?
  • @Cheersandhth.-Alf 以防止在模板参数中对其进行修改,因此它需要成为函数模板签名的一部分(因此 ABI 需要定义修改而不是仅适用于表达式,但适用于任意语句)。由于每个 expr 都是唯一类型的对象,因此我不确定是否将其放入 sizeof 或 decltype 中。这就是我的理解,但我可能是错的,我不是这方面的专家。铿锵人的理查德史密斯会知道的。
  • @JohannesSchaub-litb:感谢您的思考。我认为为 lambda 生成系统的类名很简单,只需在每个翻译单元中对它们进行编号。但是鉴于类型,无法实例化具有捕获的 lambda。另一方面,当将 lambda 传递给函数模板(参数类型推导)时,该类型可用,所以如果它是关于安全的,那么该类型需要已经是安全的。不确定用例... ;-)
  • C++20 现在允许在未计算的操作数中使用 lambda :)
【解决方案2】:

直接回答您的问题:否。

您需要使用可从任何类型分配的东西,类似于具有良好定义类型的函子。一个示例是 sbi 的答案中所示的 std::function 。然而,这不是 lambda 表达式的类型。

【讨论】:

    【解决方案3】:

    你可以使用一个小的类 lambda_wrapper,以低成本包装一个 lambda。它比 std::function 快得多,因为没有虚函数调用和动态内存分配。 Wrapper 通过推导 lambda 参数列表和返回类型来工作。

    #include <iostream>
    #include <functional>
    #include <set>
    
    template <typename T, typename ... Args>
    struct lambda_wrapper : public lambda_wrapper<decltype(&T::operator())(Args...)> {};
    
    template <typename L>
    struct lambda_wrapper<L> {
    private:
        L lambda;
    
    public:
        lambda_wrapper(const L & obj) : lambda(obj) {}
    
        template<typename... Args>
        typename std::result_of<L(Args...)>::type operator()(Args... a) {
            return this->lambda.operator()(std::forward<Args>(a)...);
        }
    
        template<typename... Args> typename
        std::result_of<const L(Args...)>::type operator()(Args... a) const {
            return this->lambda.operator()(std::forward<Args>(a)...);
        }
    };
    template <typename T>
    auto make_lambda_wrapper(T&&t) {
        return lambda_wrapper<T>(std::forward<T>(t));
    }
    int main(int argc, char ** argv) 
    {
        auto func = make_lambda_wrapper([](int y, int x) -> bool { return x>y; });
        std::set<int, decltype(func)> ss(func);
        std::cout << func(2, 4) << std::endl;
    }
    

    【讨论】:

    • auto func = make_lambda_wrapper([](int y, int x) { return x&gt;y; });auto func = [](int y, int x) { return x&gt;y; }; 更有用吗?
    • @aschepler "auto func" 类型是匿名类,lamda_wrapper 是显式类类型。因此,您可以将 lambda_wrappers 存储在 std::map 或设置中。不可能统一存储原始 lambdas 的集合,因为每个 lambdas 都是不同的类型。但是您可以定义一个 vector 并存储任何具有匹配签名的 lambda。当然你也可以用 std::function 来做,但是 std::function 更慢(vptr + atomic lock + heap alloc)
    • 不,你不能在一个容器中存储两个不同的lambda_wrappers,只能存储同一个 lambda 的两个副本。您也可以直接使用 auto lambda 来执行此操作。
    【解决方案4】:

    至少在 Microsoft Visual Studio 中(我没有用其他编译器尝试过),如果你没有捕获任何东西,类型似乎是一个常规函数指针:

    std::string (*myFunctionPointer)(int x) = [] (int x) {
      char buffer[10];
      return std::string("Test ") + itoa(x, buffer, 10);
    };
    std::string testOutput = myFunctionPointer(123);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-08-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-10-09
      相关资源
      最近更新 更多