【问题标题】:C++ high order lambda cannot deduce returned lambda typeC++ 高阶 lambda 不能推导出返回的 lambda 类型
【发布时间】:2020-03-26 12:09:06
【问题描述】:

我有一个任务是创建一个实际通用的类,该类可以获取某种整数输入源和某种消费这些输入的消费者。我已经成功地完成了这项工作,但它比我想要的更丑陋。所以这是我的解决方案的一部分。

这是我的输入源实现的接口:

 class InputSource {
   public:
      virtual const InputSource& operator<<(int& num) const = 0;
      virtual ~InputSource()=default;
 };

这是一种从键盘获取输入的实现:

class KeyboardInput : public InputSource {
   public:
      KeyboardInput()=default;
      virtual ~KeyboardInput()=default;

   virtual const InputSource& operator<<(int& num) const override {
      std::cin >> num;
      return *this;
   }
};

这是我对数字序列类的实现,它接受一些输入源并接受一个操作,该操作是一个 std::function,它对从输入源给出的数字进行操作

class NumberSequence {

const InputSource &input_source;
const std::function<void(int)> &action;

int next_num() {int num; input_source<<num; return (num<0 ? -1 : num);} // -1 if no morenumbers

public:
   NumberSequence(const InputSource &input_source, const std::function<void(int)> &action) : input_source(input_source), action(action) {}

   void start_action() {
       int num;
       do {
          action(num = next_num());
          if(num == -1) break;
          std::this_thread::sleep_for(std::chrono::seconds(1));
       } while(true);
   }
 };

这个类的肉类是 start_action 成员函数,它从给定的输入源获取输入,然后使用该数字调用操作并等待 1 秒,直到输入源给出 -1,就这么简单。

所以现在我编写了一个操作的实现,将这些数字输出到文件中,但不是作为一个类,而是作为一个 lambda,如下所示:

static auto write_to_file_action = [](std::ofstream& output_file) {

     return [&output_file](int num){
       if(num == -1) return;
       using namespace std::chrono;

       time_point<system_clock> now = system_clock::now();
       std::time_t time = system_clock::to_time_t(now);
       output_file << num <<"\t"<< std::ctime(&time) << std::endl;
    };
};

这个 lambda 将 std::ofstream 接收到我输出数字的文件中,并增加了时间,但它并不真正相关(部分时间)。所以在我的 main 函数中使用 this 是这样的:

int main(void) {
   KeyboardInput input_source;

   std::ofstream output_file("output_file.txt");
   NumberSequence num_seq(input_source, write_to_file_action(output_file));

   num_seq.start_action();

   return 0;
}

就像我说的这行得通,但我想要这样的东西:

int main(void) {
   KeyboardInput input_source;

   NumberSequence num_seq(input_source, write_to_file_action("output_file.txt"));

   num_seq.start_action();

   return 0;
}

这看起来很简单,但我很难实现它。我试过像这样实现 write_to_file_action:

static auto write_to_file_action = [](const char* file_name) {

    std::ofstream output_file(file_name);

    return [output_file = std::move(output_file)](int num) mutable {
       if(num == -1) return;
       using namespace std::chrono;

       time_point<system_clock> now = system_clock::now();
       std::time_t time = system_clock::to_time_t(now);
       output_file << num <<"\t"<< std::ctime(&time) << std::endl;
    };
};

然后我得到编译错误,基本上说这不起作用,因为我的 NumberSequence 类想要一个 std::function 但 std::function 必须是可复制的,这不是我的情况。在我的内部 lambda 的 clojure 中,我有一个 std::ofstream 这是不可复制

所以我尝试像这样对我的 NumberSequence 类进行模板化:

template<typename Func>
class NumberSequence {

    const InputSource &input_source;
    const Func action;

    int next_num() {int num; input_source<<num; return (num<0 ? -1 : num);} // -1 if no more numbers

    public:
       NumberSequence(const InputSource &input_source, Func &&action) 
           : input_source(input_source), action(std::move(action)) {}

    void start_action() {
        int num;
        do {
           action(num = next_num());
           if(num == -1) break;
           std::this_thread::sleep_for(std::chrono::seconds(1));
        } while(true);
    }
};

这不会编译,但现在它没有编译,因为它说在 main 中的 num_seq 变量之前缺少一个模板参数(它可以推断它),所以我可以这样做:

int main(void) {
    KeyboardInput input_source;

    auto lambda = write_to_file_action("output_file.txt");
    NumberSequence<decltype(lambda)> num_seq(input_source, lambda);

    num_seq.start_action();

    return 0;
}

我还需要在我的 NumberSequence 中创建另一个构造函数,它接受对操作的正常引用而不是右值引用。 就像我很难过这个作品一样,但我想删除这个显式的模板设置。我不知道这是否可能,但我看起来可能。如果有人可以解释为什么它不能隐式推断类型,因为我不明白。谢谢你。

附:抱歉发了很长的帖子,这是我第一次发帖,我想记录下我的问题的全部背景。

【问题讨论】:

  • 您创建了一个自动推断返回类型并实例化NumberSequence 类模板的函数。在 C++17 之前,使用 std::make_pair 之类的东西来代替缺少类模板参数推导。
  • 也许可以减少你的问题,包括一个minimal reproducible example,并请包括完整的编译器错误
  • [](std::ofstream&amp; output_file)std::function&lt;void(int)&gt;
  • 或者您可以通过将 std::ofstream 存储在共享指针中来使 lambda 可复制 - 哑但有效。

标签: c++ lambda higher-order-functions template-argument-deduction


【解决方案1】:

C++14 中的 lambda 自动推导方法示例。一个生成模板函数,该函数推断类型并返回适当创建的类模板。

#include <iostream>
#include <memory>

using namespace std;

    template<typename PFunc>
    class   CProcGuard
    {
    public:
        CProcGuard(PFunc&& f):mFunc(std::move(f)){};
        CProcGuard(CProcGuard&&) = default;
        ~CProcGuard()
        {
            mFunc();
        }

    private:
        PFunc mFunc;
    };

    template<typename PFunc>
    auto    ExecuteOnExit(PFunc&& f) -> CProcGuard<PFunc>
    {
        return CProcGuard<PFunc>(std::move(f)); // god bless RVO
    }

int main()
{
    std::unique_ptr<int> nonMovable =make_unique<int>(5);

    auto exitGuard = ExecuteOnExit([nm = std::move(nonMovable)]()
    {
        cout<<"Hello World " << *nm;
    });

    return 0;
}

您的第二个选择是通过std::shared_ptr 使 lambda 可移动。类似的东西:

static auto write_to_file_action = [](const char* file_name) {

    auto ptr_output_file = std::make_shared<std::ofstream>(file_name);

    return [ptr_output_file = std::move(ptr_output_file)](int num) mutable {
       if(num == -1) return;
       using namespace std::chrono;

       time_point<system_clock> now = system_clock::now();
       std::time_t time = system_clock::to_time_t(now);
       *ptr_output_file  << num <<"\t"<< std::ctime(&time) << std::endl;
    };
};

有点愚蠢和低效,但它有效。

注意:制作静态 lambda write_to_file_action 没有意义 - 只需制作一个常规函数 - 它可能会让一些人感到困惑。

【讨论】:

    猜你喜欢
    • 2014-02-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-23
    • 1970-01-01
    • 2020-11-05
    • 1970-01-01
    相关资源
    最近更新 更多