【发布时间】: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& output_file)≠std::function<void(int)> -
或者您可以通过将
std::ofstream存储在共享指针中来使 lambda 可复制 - 哑但有效。
标签: c++ lambda higher-order-functions template-argument-deduction