【问题标题】:C++ ternary assignment of lambdalambda 的 C++ 三元赋值
【发布时间】:2020-03-03 05:47:59
【问题描述】:

知道为什么下面的 sn-p 不能编译吗?它抱怨错误“错误:操作数到?:有不同的类型”

  auto lambda1 = [&](T& arg) {
      ...
  };
  auto lambda2 = [&](T& arg) {
      ...
  };
  auto lambda = condition ? lambda1 : lambda2;

【问题讨论】:

    标签: c++ c++11 lambda conditional-operator


    【解决方案1】:

    奇怪的是,如果 lambda 是无捕获的,则可以使用运算符 + 技巧:

    auto lambda1 = [](int arg) { ... };
    auto lambda2 = [](int arg) { ... };
    
    auto lambda = condition ? +lambda1 : +lambda2; // This compiles!
    lambda(2019); 
    

    这是可行的,因为+ 会将 lambda 转换为函数指针,并且两个函数指针具有相同的类型(类似于 void (*)(int))。

    使用 GCC 和 Clang(但不使用 MSVC),+ 可以省略,lambdas 仍将转换为函数指针。

    【讨论】:

    • 这在 Visual Studio 上不起作用。他们允许 lambda 转换为不同的调用转换的扩展阻止了它。
    • @GuillaumeRacicot,感谢您的留言。您能否提供一个链接,让我可以阅读更多相关信息?
    • @GuillaumeRacicot 它似乎可以在最近的 MSVC 版本上编译。 godbolt.org/z/ZQLWxy
    • @Brian 哦!这是个好消息。现在我必须更改一些代码。谢谢!
    【解决方案2】:

    编译器将各个 lambda 转换为不同的类。 例如,lambda1 的定义等价于:

    class SomeCompilerGeneratedTypeName {
    public:
      SomeCompilerGeneratedTypeName(...) { // Capture all the required variables here
      }
    
      void operator()(T& arg) const {
        // ...
      }
    
    private:
      // All the captured variables here ...
    };
    

    因此编译器生成了两种不同的类型,导致auto lambda = condition ? lambda1 : lambda2;的类型不兼容

    以下方法可行:

    auto lambda = condition ? std::function<void(T&)>(lambda1) : std::function<void(T&)>(lambda2);
    

    为了强调这两个 lambda 确实是不同的类型,我们可以使用标准库中的 &lt;typeinfo&gt;typeid 运算符。 Lambda 不是多态类型,因此标准保证在编译时评估“typeid”运算符。这表明即使 RTTI 被禁用,以下示例仍然有效:

    #include <iostream>
    #include <typeinfo>
    
    int main()
    {
        struct T {
    
        };
    
        auto lambda1 = [&](T& arg) {
            return;
        };
    
        auto lambda2 = [&](T& arg) {
          return;
        };
    
        std::cout << typeid(lambda1).name() << "/" << typeid(lambda1).hash_code() << std::endl;
        std::cout << typeid(lambda2).name() << "/" << typeid(lambda2).hash_code() << std::endl;
    
        return 0;
    }
    

    程序的输出是(使用 GCC 8.3,see on Gobolt):

    Z4mainEUlRZ4mainE1TE_/7654536205164302515
    Z4mainEUlRZ4mainE1TE0_/10614161759544824066
    

    【讨论】:

    • 完全错误是“错误:操作数?:具有不同的类型'f(const std::vector&, size_t, size_t) [with T = unsigned char; size_t = long unsigned int ]::' 和 'f(const std::vector&, size_t, size_t) [with T = unsigned char; size_t = long unsigned int]::'",我看到所有类型和格式都相同。
    • @cow 因为 lambda 本身具有相同的签名,所以编译器为了隐藏其实现细节并给出更容易理解的错误,为您提供两个相同的 lambda 的位置和签名.但最后还是被解释为SomeCompilerGeneratedTypeName1SomeCompilerGeneratedTypeName2
    • @cow 我添加了一个突出显示答案开头的示例,您可能会觉得它很有趣
    【解决方案3】:

    编译器无法决定auto 应该是什么类型:

    auto lambda = condition ? lambda1 : lambda2;
    

    因为每个 lambda 都有不同且唯一的类型。

    一种可行的方法是:

    auto lambda = [&](T& arg) {
         return (condition ? lambda1(arg) : lambda2(arg));
    }
    

    【讨论】:

      【解决方案4】:

      它无法编译,因为每个 lambda 都有唯一的类型,?: 没有通用类型。

      您可以将它们包装在 std::function&lt;void(T&amp;)&gt; 中,例如

      auto lamba1 = [&](T& arg) {
        ...
      };
      auto lambda2 = [&](T& arg) {
        ...
      };
      auto lambda = condition ? std::function(lambda1) : lambda2; // C++17 class template deduction
      

      【讨论】:

        【解决方案5】:

        由于 2 个 lambda(lambda1lambda2)是 2 种不同的类型,?: 无法从 lambda1lambda2 推断出 lambda 的返回类型。发生这种情况是因为这 2 个不能相互转换。

        【讨论】:

          猜你喜欢
          • 2013-04-05
          • 1970-01-01
          • 2013-08-13
          • 2021-04-29
          • 2021-10-07
          • 1970-01-01
          • 1970-01-01
          • 2011-07-02
          • 2012-02-14
          相关资源
          最近更新 更多