Возможны ли методы получения без шаблонов в классах контейнера шаблонов Variadic? - PullRequest
1 голос
/ 16 сентября 2011

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

int MyInt = MyTuple.Get<int>(0);

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

int MyInt = MyTuple.Get(0);

Моей первой мыслью было, чтобы функция-член Get() возвратила другой класс, который самостоятельно вычисляет тип, вероятно, сравнивая typeid(Foo).name() со значениями в некотором предварительно вычисленном списке. Это все еще должно произойти до времени выполнения, и я не мог найти способ перебрать что-то подобное во время компиляции.

Есть ли какой-нибудь способ для класса контейнера шаблона variadic - например, кортежа - иметь метод получения, который не требует явно заданного типа?

Ответы [ 7 ]

7 голосов
/ 16 сентября 2011

Вы имеете в виду, как std::tuple?

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

template< typename ... types >
struct tuple;

template< typename head, typename ... tail_types >
struct tuple {
    head value;
    tuple< tail_types ... > tail;
};

template<>
struct tuple<> {};

template< typename tuple, size_t index >
struct tuple_element;

template< typename head, typename ... tail, size_t index >
struct tuple_element< tuple< head, tail ... >, index >
    { typedef typename tuple_element< tail ..., index - 1 >::type type; };

template< typename head, typename ... tail >
struct tuple_element< tuple< head, tail ... >, 0 >
    { typedef head type; };

template< size_t index, typename tuple >
typename std::enable_if< index != 0, 
                   typename tuple_element< tuple, index >::type >::type
get( tuple const &t )
    { return get< index - 1 >( t.tail ); }

template< size_t index, typename tuple >
typename std::enable_if< index == 0,
                   typename tuple_element< tuple, index >::type >::type
get( tuple const &t )
    { return t.value; }

и т.д.

3 голосов
/ 16 сентября 2011

Правила C ++ обычно не допускают того, что вы запрашиваете.

Структура - это набор значений известных типов, доступ к которым осуществляется по имени, которое должно быть предоставлено во время компиляции.Эта последняя часть имеет решающее значение, поскольку она позволяет компилятору C ++ знать, о каком значении вы говорите.

A tuple представляет собой набор значений известных типов, доступ к которым осуществляется по номеру ..Однако то, что это число, не означает, что оно может быть числом, определяемым во время выполнения, так же как наличие строкового имени для члена структуры не означает, что вы можете получить этот член во время выполнения.

Каждый членtuple набрано, и C ++ должен знать типы вещей во время компиляции, чтобы функционировать.Точно так же, тип возврата любой функции известен во время компиляции; не может меняться в зависимости от значения параметра.Вы можете выбрать другую функцию в зависимости от типа (перегрузка функции или вычет аргумента шаблона), но она не может быть основана на значении, потому что значения (если они не являются константами времени компиляции) известны только во время выполнения.

Чтобы сделать то, что вы хотите, вам придется каким-то образом сломать систему типов C ++.Например, теоретически у вас может быть функция get, которая возвращает указатель без типа.Или, если вы хотите хоть какую-то претензию на безопасность типов, возможно, boost::any или boost::variant типов tuple.Но это самое лучшее, что вы сможете сделать.

Даже если вы укажете тип во время компиляции (get<Type>), этого все равно недостаточно, из-за возможности несоответствия типов.Что вы тогда делаете?

Эта концепция просто не будет работать на типизированном языке времени компиляции, таком как C ++.

1 голос
/ 16 сентября 2011

Итак, вы уже получили несколько ответов, объясняющих, что это сработает, если вы передадите индекс в качестве параметра времени компиляции.Я хочу прокомментировать общую идею, однако:

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

template<typename T> operator T()

(я тожеХотя лениво проверять это в коде. Если кто-то готов выполнить работу и показать рабочий пример, напишите мне комментарий здесь, и я, скорее всего, возбуждаю.)

Конечно, как всегда с такими хакинужно спросить себя, стоит ли оно тогоНеявное преобразование может сбить вас с толку в любое время.(Раньше я думал, что иногда они не являются злом. Но однажды мне пришлось удалить последнее неявное преобразование в моем рабочем коде, потому что он привел к очень трудно найти ошибку, и поклялся никогда не делатьиспользуйте их снова.)
Кроме того, дополнительная безопасность, связанная с указанием типа, может выступать в роли ремня безопасности (не позволяя вам протиснуться через лобовое стекло в случае серьезного сбоя).
Наконец, C ++ 11 позволяет это:

auto foo = bar.get<int>(); // available today at a compiler near you

Видите, тип пишется только один раз.:)

1 голос
/ 16 сентября 2011

Стандартная библиотека включает в себя std::get шаблон функции, который работает с std::tuple и std::pair и не требует, чтобы тип был задан явно.Он работает, принимая индекс в качестве параметра шаблона .

std::tuple<int,std::string,char const*> t(1,"foo","blah");
auto i = std::get<0>(t); // i is int
auto s = std::get<1>(t); // s is std::string
auto p = std::get<2>(t); // p is char const*

. Тип возвращаемого значения всегда должен быть известен во время компиляции, и это зависит от индекса, поэтому вы не можете принятьиндекс как аргумент времени выполнения.

1 голос
/ 16 сентября 2011

Это не гипотетически.boost :: tuple ведет себя так, как вы описываете.

boost::tuple<int, float> myTuple = boost::make_tuple(10, 3.1);
int myInt = myTuple.Get<0>();
1 голос
/ 16 сентября 2011

MyTuple.Get(0); Каким будет тип возврата такого объявления Get? Если вы не согласны с тем, что он возвращает вариант для каждого возможного типа в кортеже, то вы можете заставить его работать, да.

С другой стороны, Boost.Tuple и C ++ 0x Tuple принимают индекс в качестве параметра шаблона. Там нет возможности для ошибок там. Проверьте их.

0 голосов
/ 16 сентября 2011

Вы, конечно, можете это сделать, но, вероятно, оно того не стоит. Один из способов сделать это - предоставить статическую функцию, которая преобразует элемент экземпляра шаблона переменной в boost::any и имеет статическую таблицу функций:

template <...> class any_tuple 
{
...
    typedef boost::any (*extract_member)(any_tuple &);
    static extract_member member_extrators[...];

    boost::any Get(int index) { return member_extractors[index](*this); }
};

Но тогда вам нужно беспокоиться об инициализации и прочем.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...