Function argument returning void or non-void type

后端 未结 4 1044
心在旅途
心在旅途 2021-01-17 10:37

I am in the middle of writing some generic code for a future library. I came across the following problem inside a template function. Consider the code below:



        
4条回答
  •  暖寄归人
    2021-01-17 11:29

    The best way to do this, in my opinion, is to actually change the way you call your possibly-void-returning functions. Basically, we change the ones that return void to instead return some class type Void that is, for all intents and purposes, the same thing and no users really are going to care.

    struct Void { };
    

    All we need to do is to wrap the invocation. The following uses C++17 names (std::invoke and std::invoke_result_t) but they're all implementable in C++14 without too much fuss:

    // normal case: R isn't void
    template ,
        std::enable_if_t::value, int> = 0>
    R invoke_void(F&& f, Args&&... args) {
        return std::invoke(std::forward(f), std::forward(args)...);
    }
    
    // special case: R is void
    template ,
        std::enable_if_t::value, int> = 0>
    Void invoke_void(F&& f, Args&&... args) {
        // just call it, since it doesn't return anything
        std::invoke(std::forward(f), std::forward(args)...);
    
        // and return Void
        return Void{};
    }
    

    The advantage of doing it this way is that you can just directly write the code you wanted to write to begin with, in the way you wanted to write it:

    template
    auto foo(F &&f) {
        auto result = invoke_void(std::forward(f), /*some args*/);
        //do some generic stuff
        return result;
    }
    

    And you don't have to either shove all your logic in a destructor or duplicate all of your logic by doing specialization. At the cost of foo([]{}) returning Void instead of void, which isn't much of a cost.

    And then if Regular Void is ever adopted, all you have to do is swap out invoke_void for std::invoke.

提交回复
热议问题