Тип возвращаемой переменной в зависимости от пакета параметров sizeof…

StackOverflow https://stackoverflow.com//questions/21039644

Вопрос

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

Например, я хотел бы foo<int>() вернуть int и foo<int, float> вернуть что-то с типом std::tuple<int, float>.

Все мои попытки добиться такого эффекта не увенчались успехом.

Рассмотрим следующий подход, используя структура типа типа:

template<typename... T>
struct return_type {
    typedef std::tuple<T...> type;
};

template<>
struct return_type<int> {
    typedef int type;
};

// ... insert partial specializations for other supported primitive types

template<typename... T>
auto foo() -> typename return_type<T...>::type {
    if (sizeof...(T) == 1)
        return zap<T...>(); // Returns something of type T, where T is the first parameter
    else
        return bar<T...>(); // Assume this returns a std::tuple<T...>
}

Это не удастся скомпилировать из-за разных типов возвращаемых значений в теле foo.

Альтернативно, вот попытка использования decltype:

<template T>
T singular();

<template... T>
std::tuple<T...> multiple();

template <typename... T>
auto foo() -> decltype(sizeof...(T) == 1 ? singular() : multiple())
{
    ... // as above
}

Это также не удастся скомпилировать, поскольку тернарный оператор ожидает, что обе ветви вернут один и тот же тип.

Наконец, наивный подход с использованием простой рекурсивной распаковки также терпит неудачу:

template<typename T>
T foo() { return T{}; // return something of type T }

template<typename... T>
std::tuple<T...> foo() { return bar<T...>(); // returns a tuple }

Это, конечно, не удается, поскольку компилятор не может определить, какую перегруженную функцию вызывать.

Я не понимаю, почему нечто подобное невозможно в C++11, поскольку вся информация, необходимая для определения типа возвращаемого значения, доступна во время компиляции.И все же я изо всех сил пытаюсь понять, какие инструменты позволят мне это сделать.Любая помощь и предложения будут оценены по достоинству.

Это было полезно?

Решение 2

Ух, я буквально нашел ответ после того, как наконец сделал перерыв и выпил (это тоже после пары часов попыток найти решение!).

Ответом является модификация последнего подхода:

template<typename T>
T foo() { return zap<T>(); /* returns a T */ }

template<typename T1, typename T2, typename... Ts>
std::tuple<T1, T2, Ts...> foo() { return bar<T1, T2, Ts...>(); /* returns a tuple */ }

Используя два фиктивных параметра, компилятор может однозначно решить, какую функцию следует вызвать.

Другие советы

Обычно я использую структуру для специализаций:

#include <iostream>
#include <tuple>

namespace Detail {
    template <typename...Ts>
    struct Foo {
        typedef std::tuple<Ts...> return_type;
        static return_type apply() { return return_type(); }
    };

    template <typename T>
    struct Foo<T> {
        typedef T return_type;
        static return_type apply() { return return_type(); }
    };
}
template <typename...Ts>
typename Detail::Foo<Ts...>::return_type foo() {
    return Detail::Foo<Ts...>::apply();
}

int main ()
{
    std::tuple<int, int> t = foo<int, int>();
    int i = foo<int>();
}

Я бы использовал диспетчеризацию тегов.

template<class...Ts> struct many :std::true_type {};
template<class T>struct many :std::false_type {};

template<class...Ts> struct return_value {
  typedef std::tuple< typename std::decay<Ts>::type... > type;
};
template<class T>struct return_value : std::decay<T> {};

template<typename T>
T singular( T&& t ) {
  return std::forward<T>(t);
}
template<typename... Ts>
typename return_value<Ts...>::type multiple( Ts&&... ts ) {
  return { std::forward<Ts>(ts)... };
}

template<typename...T>
typename return_value<T...>::type worker(std::false_type, T&&...t ) {
  static_assert( sizeof...(T)==1, "this override is only valid with one element in the parameter pack" );
  return singular(std::forward<T>(t)...);
}

template<typename...Ts>
typename return_value<Ts...>::type worker(std::true_type, Ts&&...t) {
  return multiple(std::forward<Ts>(t)...);
}

template<class...Ts>
auto foo(Ts&&... t)
-> decltype(worker( many<Ts...>(), std::declval<Ts>()...) )
{
  return worker(many<Ts...>(), std::forward<Ts>(t)...);
}

тогда я бы добавил идеальную переадресацию.

Мне легче рассуждать о перегрузках, чем о специализациях.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top