我正在写一个 Option
表示可能存在或可能不存在的值的类。 if_opt
函数旨在采用 Option
和一个将根据 Option
中保存的值调用的函数,但前提是该值存在。
template <class T>
class Option {
private:
std::shared_ptr<T> m_value;
public:
explicit operator bool()const noexcept
{
return (bool)m_value;
}
Option() = default;
explicit Option(T value)
{
m_value = std::make_shared<T>(value);
}
template <class U>
friend void if_opt(Option<U>&, std::function<void(U&)>);
};
template <class T>
void if_opt(Option<T>& opt, std::function<void(T&)> f)
{
if (opt) f(*opt.m_value);
};
我注意到如果我这样使用它,它会起作用:
Option<int> none;
Option<int> some(10);
function<void(int&)> f1 = [](int& none)
{
cout << "This should never be reached" << endl;
};
function<void(int&)> f2 = [](int& some)
{
cout << "The value of some is: " << some << endl;
};
if_opt(none, f1);
if_opt(some, f2);
但我希望能够将 lambda 表达式直接放在调用中,但是当我这样做时:
if_opt(none, [](int&)
{
cout << "This should never be reached" << endl;
});
if_opt(some, [](int& some)
{
cout << "The value of some is: " << some << endl;
});
我得到一个错误:
error: no matching function for call to 'if_opt(Option<int>&, main()::<lambda(int&)>)'
我知道 lambda 表达式的类型在标准中是未定义的,它只需要分配给 std::function<R(T)>
,所以这很有意义,但是有没有办法让 lambda 参数隐式转换为 std::function<void(T&)>
这样我就可以在对 if_opt
的调用中定义 lambda我尝试的方式?
最佳答案
std::function<Sig>
是一种类型删除工具。它删除(几乎)所有关于它存储的值的所有信息,它可以用 Sig
调用。 .
模板参数推导采用传入的类型并推导应使用的类型,然后生成并(通常)调用模板函数。
这些几乎是彼此相反的。在类型删除模板上进行推导是代码味道,而且几乎总是一个坏主意。
所以这是你的基本设计错误。
修复代码的方法有很多种。
首先,if_opt
不应该是模板。
friend void if_opt(Option<T>& opt, std::function<void(T&)> f){
if (opt) f(*opt.m_value);
}
这创建了我所说的 Koenig friend 。您必须内联定义主体。真的,那个U
类型毫无意义,在某些情况下甚至会导致错误。
但是这里的类型删除也是毫无意义的。修复返回模板,但现在有充分的理由。
template<class F>
friend void if_opt(Option<T>& opt, F&& f){
if (opt) f(*opt.m_value);
}
这是一个更好的设计。
你可以去投资 SFINAE 重载解析代码,但我不会费心。
template<class F,
std::conditional_t<true, bool,std::result_of_t<F&(T&)>> = true
>
friend void if_opt(Option<T>& opt, F&& f){
if (opt) f(*opt.m_value);
}
以上是晦涩的,并且与上面的相比具有最小的边际优势。
关于c++ - 将 lambda 直接传递给函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48969458/