如何在std::function中使用默认参数?
这个问题我之前踩过坑,其实核心原因很好理解:std::function只认你给它指定的函数签名,完全不管它存储的可调用对象(比如你的lambda)有没有默认参数。
你看,你定义的std::function<void(const string&)>,它的调用接口是固定死的——必须传入一个const string&类型的参数。虽然你给lambda加了默认参数,但这个默认参数是lambda自己的“小特性”,不会被std::function的类型系统“看”到。std::function只关心你给它的模板参数里的签名,也就是void(const string&),所以当你尝试无参调用print()时,编译器会认为你没给这个需要参数的函数传值,直接报错。
而普通函数::print()能无参调用,是因为编译器在编译期就知道它有默认参数,会自动帮你把::print()补全成::print("hello world!"),但std::function的调用逻辑是运行时通过类型擦除实现的,编译器没法提前帮你补全参数。
那怎么解决这个问题呢?给你几个实用的办法:
办法1:显式传递默认参数(最简单)
就像你代码里已经做的那样,直接调用print("hello world!")。如果觉得写全默认值麻烦,可以把默认值抽成一个常量,比如:
const std::string DEFAULT_MSG = "hello world!"; // 定义lambda和std::function的时候用这个常量 function<void(const string&)> print = [&DEFAULT_MSG](const string& rS = DEFAULT_MSG) -> void { cout << rS << endl; }; // 调用的时候要么传参,要么显式用常量 print(DEFAULT_MSG);
办法2:调整std::function的签名,支持无参调用
如果你主要需要无参调用,偶尔才传参,可以把std::function的类型改成std::function<void()>,但这样就不能传参数了。要是想同时支持两种调用方式,可以定义两个std::function对象:
// 无参版本 function<void()> print_no_arg = [](){ cout << "hello world!" << endl; }; // 带参版本 function<void(const string&)> print_with_arg = [](const string& rS){ cout << rS << endl; }; // 调用的时候按需使用 print_no_arg(); print_with_arg("custom message");
办法3:用std::optional包装参数(C++17及以上可用)
如果想让同一个std::function既能无参又能带参,可以用std::optional来包装参数,让参数变成可选的:
#include <optional> // 记得加头文件 function<void(std::optional<std::string>)> print = [](std::optional<std::string> opt = std::nullopt) -> void { if (opt.has_value()) { cout << opt.value() << endl; } else { cout << "hello world!" << endl; } }; // 两种调用方式都支持 print(); // 无参,用默认值 print("custom message"); // 传参
办法4:用自定义的重载函数对象
如果你的C++版本低于17,还可以写一个重载了operator()的结构体,然后把它赋值给不同签名的std::function:
struct PrintWrapper { void operator()() const { cout << "hello world!" << endl; } void operator()(const std::string& rS) const { cout << rS << endl; } }; // 定义两个std::function分别对应重载 function<void()> print_no_arg = PrintWrapper{}; function<void(const string&)> print_with_arg = PrintWrapper{}; // 调用方式和之前一样 print_no_arg(); print_with_arg("custom message");
备注:内容来源于stack exchange,提问作者Vinod

