简体   繁体   中英

Storing return values of functions in a tuple

Consider

#include <tuple>

template <typename... F>
auto execute (F... f) {
    return std::make_tuple(f(0)...);
}

int foo(int) { return 5; }
int bar(int) { return 3; }

int main() {
    auto tuple = execute(foo, bar);
}

What is a good workaround so that bar can return void?

I tried this, but it won't compile:

#include <tuple>

struct Void { };

template <typename T>
T check(T n) { return n; }

Void check(void) { return Void{}; }

template <typename... F>
auto execute (F... f) {
    return std::make_tuple(check(f(0))...);
}

int foo(int) { return 5; }
void bar(int) { }

int main() {
    auto tuple = execute(foo, bar);
}

Update: I have a tentative solution, but it only works if we know that the arguments passed is always int 0. I'll try to make it work in any general setting. Or perhaps use std::optional like Steve suggested.

#include <tuple>
#include <type_traits>

struct Void { };

template <typename F>
auto execute_h (F f, std::enable_if_t<!std::is_void_v<std::result_of_t<F(int)>>>* = nullptr) {
    const auto result = f(0);
    return result;  
}

template <typename F>
auto execute_h (F f, std::enable_if_t<std::is_void_v<std::result_of_t<F(int)>>>* = nullptr) {
    f(0);
    return Void{};  
}

template <typename... F>
auto execute (F... f) {
    return std::make_tuple(execute_h(f)...);
}

int foo(int) { return 5; }
void bar(int) { }

int main() {
    auto tuple = execute(foo, bar);
}

With abusing overload of operator , (as void(), T won't call custom operator, ):

#include <tuple>

struct Void {};

template <typename T>
T&& operator , (T&& t, Void) { return std::forward<T>(t); }

template <typename... Fs>
auto execute (Fs... fs) {
    return std::make_tuple((f(0), Void{})...);
}

int foo(int) { return 5; }
void bar(int) { }

int main() {
    auto tuple = execute(foo, bar); // tuple<int, Void>
}

You can use std::enable_if and a wrapper function to return a Void object for functors that return void :

#include <tuple>
#include <type_traits>

struct Void { };

template <typename Func, typename... Args>
auto check(Func func, Args&&... args)
    -> std::enable_if_t<!std::is_void<decltype(func(std::forward<Args>(args)...))>::value, decltype(func(std::forward<Args>(args)...))>
{
    return func(std::forward<Args>(args)...);
}

template <typename Func, typename... Args>
auto check(Func func, Args&&... args)
    -> std::enable_if_t<std::is_void<decltype(func(std::forward<Args>(args)...))>::value, Void>
{
    func(std::forward<Args>(args)...); return Void{};
}

template <typename... F>
auto execute (F... f) {
    return std::make_tuple(check(f, 0)...);
}

int foo(int) { return 5; }
void bar(int) { }

int main() {
    auto tuple = execute(foo, bar);
}

Live demo

It's a bit repetitive, but it gets the job done for any type of functor, any argument list, and any return type.

It's worth noting that there's a proposal floating around to make void a regular type that would let you avoid all of these hassles, but I'm not sure what the status of that is or if it will ever be accepted.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM