c++函数多态性/模板
我的目标是提供三种调用函数的方法listen()。有一个更好的方法吗?(比如使用模板或默认值参数,所以我只能实现一个listen()功能)
我目前的方法
void listen(const int &port,
const std::function<void(std::string err)> &f) {
http_server_.listen(port, [f](std::string err) { f(err); });
}
void listen(const int &port, const std::function<void()> &f) {
http_server_.listen(port, [f](std::string err) { f(); });
}
void listen(const int &port) {
http_server_.listen(port, [](std::string err) {});
}
我已经尝试了看起来像的模板方法
template <typename Func> void listen(const int &port, Func f = {}) {
// do some if-else here.
}
但它不起作用。
回答
IMO 你的方法接近最佳。如果我在写这个,我只会做一些小的改动:
void listen(int port, std::function<void(const std::string &err)> f)
{
http_server_.listen(port, std::move(f));
}
void listen(int port, std::function<void()> f)
{
http_server_.listen(port, [f = std::move(f)](const std::string &){f();});
}
void listen(int port)
{
// Are you sure the lambda is needed? Maybe passing `nullptr` will work.
http_server_.listen(port, [](const std::string &){});
}
在这里,f = std::move(f)(而不是f)在 lambda 捕获中至少需要 C++14。如果您仅限于 C++11 并且不能这样做,那么您应该改f回由 const 引用传递(在第二个重载中)。
在 C++17 之前,将这些组合成一个函数是不容易的,并且需要更多的样板,而不是它要消除的。
在 C++17 中你可以使用if constexpr,但如果你问我,它仍然只会让事情变得更糟:
template <typename, typename...> struct always_false : std::false_type {};
template <typename F = std::nullptr_t>
void listen(int port, F &&f = nullptr)
{
if constexpr (std::is_nullptr_v<std::remove_const_t<std::remove_reference_t<F>>>)
{
http_server_.listen(port, [](const std::string &) {});
}
else
{
http_server_.listen(port, [f = std::forward<F>(f)](const std::string &err)
{
if constexpr (std::is_invocable_v<const F &, const std::string &>)
std::invoke(f, err);
else if constexpr (std::is_invocable_v<const F &>)
std::invoke(f);
else
static_assert(always_false<F>, "The function is invalid.");
});
}
}
这不仅需要两倍的输入,还可以防止调用者使用 SFINAE 检查您的函数接受或不接受哪些函数参数。