2014-10-19 3 views
3

Я пытаюсь предоставить две версии одной и той же шаблонной функции, одну для чего-либо, что может действовать как tuple, то есть что-то, что работает с tuple_size и std::get. Мне все равно, если он получит std::array s, если он получает кортеж и пару. Это не проблема, чтобы написать общую версию, но я не могу представить, как кортеж похож на версию.шаблонная перегрузка для кортежей типа (кортеж, пара)

// 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) { /* ... */ } 

с примерами:

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 
} 

Я знаю, что я мог бы написать несколько перегруженных для кортежа, но это много письменной форме, и будет генерировать промежуточный в случае pair. Я не хочу потерять ссылку на исходную пару, поскольку это просто функция пересылки.

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) { } 

Есть в любом случае я могу сделать вышеуказанные вызовы в main работать так, как я хочу, с одной общей функцией переадресации?

Обновление: типы возврата этих функций различаются. должен был включить, что во-первых, я упрощен.

ответ

6
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


Альтернативное решение с тегом диспетчеризации:

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

+4

+1, но тег диспетчерская будет чище, чем SFINAE. – Yakk

2

Сделайте второй шаблон функции один лучший матч, если он работает:

// 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. Концепция может быть обобщена - this article от Xeo объясняет это довольно хорошо.

Смежные вопросы