这个ppt讲的是如何把返回值用lambda包起来,让返回值auto,用作者的图来总结这篇内容

1556158688788

首先思考在Context下的调用

void callWithin(const std::function<void()>& fn){
    ScopedContext context;
    try{
        fn();
    } catch (SomeException& e){
        // handle exception here
    }
}

void printLine(const std::string& text){
    std::cout<<text<<'\n';
}
callWithin([](){printLine("Hello, CppCon");});

回调小函数扔到lambda里 接口都操作lambda

也可以把这个变成模板, 这样 接口可以是任何类型的std::function, lambda

template <typename Callable>
void callWithin(const Callable& fn){
    ScopedContext context;
    fn();
}

进一步,如果想要回调函数的返回值,不需要要变动lambda接口

double sum(double a,double b){return a+b;}
double res = callWithin([](){return sum(3.14,2.71);})

可以在callWithin里改动lambda/function接口,但这降低了灵活性

double callWithin(const std::function<double()>&fn)...//如果返回值不是double怎么办?

解决办法,template auto-decltype

template <typename Callable> 
auto callWithin(const Callable& fn) ->decltype(fn()){
   decltype(fn()) result{};
   auto wrapperFn =[&]()->void
   {
       result = fn();
   }
   callWithImpl(wrapperFn);
   return result;
}
void callWithinImpl(const std::function<void()>& fn);

注意,这里用局部变量封一层result,弄一个局部的lambda,然后扔进callWithinImpl里,本质是加了一层,把原来的lambda放return的方案内嵌处理

传统方法,context肯定会有context manager,通过manager类接口来搞定, 接口也是固定的

class Contextmanager{
public:
    virtual void callWithin(const std::function<void()>&fn) = 0;
};

然后整合上面的实现,大致这样

class Contextmanager{
public:
  template <typename Fn>
  void callWithin(const Fn& fn, std::false_type) -> decltype(fn())
  {
    decltype(fn()) result{};
    callWithinImpl([&]{result=fn();});
    return result;
  }
private:
    virtual void callWithinImpl(const std::function<void()>&fn) = 0;
};

double result = manager->CallWithin([]{return sum(3.14, 2/71);});

这个方案又有了新问题,原来的直接传void返回的functor不能工作了

特化吧

template <typename Fn>
auto callWithin(const Fn& fn) -> decltype(fn())
{
    return _callWithin(fn, std::is_same<decltype(fn()),void>());
}
// true就直接调用,没有返回值
template <typename Fn>
void _callWithin(const Fn& fn, std::true_type) -> decltype(fn())
{
    callWithinImpl([&]{fn();});
}

template <typename Fn>
void _callWithin(const Fn& fn, std::false_type) -> decltype(fn())
{
    decltype(fn()) result{};
    callWithinImpl([&]{result=fn();});
    return result;
}

新的挑战,callWithin失败

所以还是需要内部callWithinImpl有个返回值,来设定result,需要std::optional 包装一下

template <typename Fn>
void callWithin(const Fn& fn) -> std::optional<decltype(fn())>
{
    decltype(fn()) result{};
    bool ok = callWithinImpl([&]{result=fn();});
    if (ok)
    	return result;
    else
        return std::nullopt;
}

ref