Function argument returning void or non-void type

后端 未结 4 1081
心在旅途
心在旅途 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:18

    In case you need to use result (in non-void cases) in "some generic stuff", I propose a if constexpr based solution (so, unfortunately, not before C++17).

    Not really elegant, to be honest.

    First of all, detect the "true return type" of f (given the arguments)

    using TR_t = std::invoke_result_t;
    

    Next a constexpr variable to see if the returned type is void (just to simplify a little the following code)

    constexpr bool isVoidTR { std::is_same_v };
    

    Now we define a (potentially) "fake return type": int when the true return type is void, the TR_t otherwise

    using FR_t = std::conditional_t;
    

    Then we define the a smart pointer to the result value as pointer to the "fake return type" (so int in void case)

    std::unique_ptr  pResult;
    

    Passing through a pointer, instead of a simple variable of type "fake return type", we can operate also when TR_t isn't default constructible or not assignable (limits, pointed by Barry (thanks), of the first version of this answer).

    Now, using if constexpr, the two case to exec f (this, IMHO, is the ugliest part because we have to write two times the same f invocation)

    if constexpr ( isVoidTR )
       std::forward(f)(std::forward(args)...);
    else
       pResult.reset(new TR_t{std::forward(f)(std::forward(args)...)});
    

    After this, the "some generic stuff" that can use result (in non-void cases) and also `isVoidTR).

    To conclude, another if constexpr

    if constexpr ( isVoidTR )
       return;
    else
       return *pResult;
    

    As pointed by Barry, this solution has some important downsides because (not void cases)

    • require an allocation
    • require an extra copy in correspondence of the return
    • doesn't works at all if the TR_t (the type returned by f()) is a reference type

    Anyway, the following is a full compiling C++17 example

    #include 
    #include 
    
    template 
    auto foo (F && f, As && ... args)
     {
       // true return type
       using TR_t = std::invoke_result_t;
    
       constexpr bool isVoidTR { std::is_same_v };
    
       // (possibly) fake return type
       using FR_t = std::conditional_t;
    
       std::unique_ptr  pResult;
    
       if constexpr ( isVoidTR )
          std::forward(f)(std::forward(args)...);
       else
          pResult.reset(new TR_t{std::forward(f)(std::forward(args)...)});
    
       // some generic stuff (potentially depending from result,
       // in non-void cases)
    
       if constexpr ( isVoidTR )
          return;
       else
          return *pResult;
     }
    
    int main ()
     {
       foo([](){});
    
       //auto a { foo([](){}) };  // compilation error: foo() is void
    
       auto b { foo([](auto a0, auto...){ return a0; }, 1, 2l, 3ll) };
    
       static_assert( std::is_same_v );
    
     }
    

提交回复
热议问题