版本限制:C++17。
我正在尝试创建一种类型,该类型能够接受任何类型的可调用对象并包装其成员函数之一(在本例中为operator()
)以采用相同的参数,但修改(强制转换)返回类型。一个例子如下:
template <typename Ret, typename Callable>
struct ReturnConverter : Callable
{
ReturnConverter(Callable cb) : Callable(cb) { }
Ret operator()(argsof(Callable::operator()) args) // magic happens here
{
return static_cast<Ret>(Callable::operator()(std::forward<??>(args)); // how to forward?
}
};
template <typename Ret, typename Callable>
auto make_converter(Callable cb)
{
return ReturnConverter<Ret, Callable>(cb);
}
int main()
{
auto callable = []() { return 1.0f; };
auto converted = make_converter<int>(callable);
auto x = converted(); // decltype(x) = int
}
ReturnConverter
可以获取一个对象并覆盖该对象operator()
以将它返回的任何内容转换为Ret
.
问题在于表达包装函数的参数类型 - 它们应该与Callable::operator()
. 使用可变参数模板std::forward
不能满足这个目标,因为它会修改函数的签名(operator()
现在变成了以前没有的模板)。
我如何表达argsof
我上面强调的运算符?
动机:我想修改本文std::visit
中演示的重载技术,以便能够从多个 lambda 仿函数中指定所需的返回类型,这样我就不必严格匹配每个 lambda 中的返回类型,例如:
std::variant<int, float, void*> v = ...;
auto stringify = overload(
[](int x) { return "int: " + std::to_string(x); },
[](float x) { return "float: " + std::to_string(x); },
[](auto v) { return "invalid type!"; } // error! const char* != std::string
);
std::visit(stringify, v);
有了上面的改变,我就可以写出类似的东西auto stringify = overload<std::string>(...);