Template overloading for tuple types (tuple, pair)

I am trying to provide two versions of the same templated function, one for anything that can act like tuple

, that is, something that works with tuple_size

and std::get

. I don't care if he gets std::array

s, if he gets a tuple and a pair. It's not a problem to write a generic version, but I can't seem to get a tuple-like version.

// 1
template <typename T>
A func(T&& t) { /* ... */ }

// 2
template <typename TupleType,
          std::size_t Size=std::tuple_size<std::decay_t<TupleType>>::value>
B func(TupleType&& tup) { /* ... */ }

      

with examples:

int main() {
    int i;
    std::vector<int> v;
    auto t = std::make_tuple(1, 2.0, 'a');
    auto p = std::make_tuple(1, 2.0);

    A a = func(i); // calls 1
    a = func(v); // calls 1

    B b = func(t); // ambiguous, should call 2
    b = func(p); // ambiguous, should call 2
}

      

I know I could write a few overloads for a tuple, but I wrote this a lot and would create an intermediate in case pair

. I don't want to lose the reference to the original pair as this is just a redirect function.

template <typename... Ts>
B func(std::tuple<Ts...>& tup) { }

template <typename... Ts>
B func(const std::tuple<Ts...>& tup) { }

template <typename... Ts>
B func(std::tuple<Ts...>&& tup) { }

      

Is there a way so that I can make the above calls in the main

way I want with one generic forwarding function?

Update : The return types of these functions are different. should have included that first, I am oversimplified.

+3


source to share


2 answers


template <typename T, typename U = void>
struct is_tuple_like : std::false_type {};

template <typename T>
struct is_tuple_like<T, decltype((void)std::get<0>(std::declval<T>()), (void)std::tuple_size<T>::value)> : std::true_type {};

template <typename T, std::enable_if_t<!is_tuple_like<std::decay_t<T>>{}, int> = 0>
A func(T&& t)
{
    return {};
}

template <typename TupleType, std::enable_if_t<is_tuple_like<std::decay_t<TupleType>>{}, int> = 0>
B func(TupleType&& tup)
{
    return {};
}

      

DEMO




Alternative solution with tag manager:

template <typename T>
A _func(T&& t, std::false_type)
{
    return {};
}

template <typename TupleType>
B _func(TupleType&& tup, std::true_type)
{
    return {};
}

template <typename TupleOrNot>
auto func(TupleOrNot&& t)
{
    return _func(std::forward<TupleOrNot>(t), is_tuple_like<std::decay_t<TupleOrNot>>{});
}

      

DEMO 2

+6


source


Make the second function template more appropriate if it works:

// 1
template <typename T>
void func_(T&& t, long) { /* ... */ }

// 2
template <typename TupleType,
          std::size_t Size=std::tuple_size<std::decay_t<TupleType>>::value>
void func_(TupleType&& tup, int) { /* ... */ }

template <typename T>
void func(T&& t) { func_(std::forward<T>(t), 0); }

      



Demo . The concept can be generalized - this article from Xeo explains it pretty well.

+2


source







All Articles