Использование auto и decltype в C++11
Вопрос
Я пытаюсь изучить принятые в настоящее время функции С++ 11, и у меня возникают проблемы с auto и decltype.В качестве учебного упражнения я расширяю список классов std некоторыми общими функциями.
template<class _Ty, class _Ax = allocator<_Ty>>
class FList : public std::list<_Ty, _Ax>
{
public:
void iter(const function<void (_Ty)>& f)
{
for_each(begin(), end(), f);
}
auto map(const function<float (_Ty)>& f) -> FList<float>*
{
auto temp = new FList<float>();
for (auto i = begin(); i != end(); i++)
temp->push_back(f(*i));
return temp;
}
};
auto *ints = new FList<int>();
ints->push_back(2);
ints->iter([](int i) { cout << i; });
auto *floats = ints->map([](int i) { return (float)i; });
floats->iter([](float i) { cout << i; });
Для карты элементов я хочу, чтобы тип возвращаемого значения был универсальным в зависимости от того, что возвращает переданная функция.Итак, для возвращаемого типа я мог бы сделать что-то вроде этого.
auto map(const function<float (_Ty)>& f) -> FList<decltype(f(_Ty))>*
Также потребуется удалить тип float в шаблоне функции.
auto map(const function<auto (_Ty)>& f) -> FList<decltype(f(_Ty))>*
Я мог бы использовать класс шаблона, но это делает использование экземпляров более подробным, поскольку мне нужно указать тип возвращаемого значения.
template<class T> FList<T>* map(const function<T (_Ty)>& f)
Подводя итог моему вопросу, я пытаюсь выяснить, как определить карту без использования класса шаблона и при этом сохранить ее общий тип, который она возвращает.
Решение
Получение из std::list
или другой std::
контейнеры не рекомендуется.
Пишите свои операции как свободные функции, чтобы они могли работать с любым стандартным контейнером через итераторы.
Вы имеете в виду «определить карту без использования функции шаблона»?
Вы должны уметь использовать result_type
тип члена std::function
чтобы получить тип, который он возвращает.
Также вам не обязательно указывать, что функция передается как std::function
.Вы можете оставить его открытым как любой тип и позволить компилятору объединить все.Вам нужно только std::function
для полиморфизма времени выполнения.
А использование new для создания необработанных объектов распределения кучи и возврат их по указателю — это ооочень 1992 год!:)
Ваша функция iter по сути то же самое, что и функция цикл for на основе диапазона.
Но все это в стороне...ты имеешь в виду что-то вроде этого?
template <class TFunc>
auto map(const TFunc &f) -> FList<decltype(f(_Ty()))>*
{
auto temp = new FList<decltype(f(_Ty()))>();
for (auto i = begin(); i != end(); i++)
temp->push_back(f(*i));
return temp;
}
Это будет соответствовать всему, что можно вызвать, и определит тип возвращаемого значения функции с помощью decltype.
Обратите внимание, что для этого требуется, чтобы _Ty был конструируемым по умолчанию.Вы можете обойти это, создав экземпляр:
template <class T>
T make_instance();
Никакой реализации не требуется, поскольку не генерируется код, вызывающий ее, поэтому компоновщику не на что жаловаться (спасибо dribeas за указание на это!)
Итак, код теперь становится:
FList<decltype(f(make_instance<_Ty>()))>*
Или, буквально, список любого типа, который вы можете получить, вызвав функцию f со ссылкой на экземпляр _Ty.
И в качестве бесплатного бонуса за согласие найдите ссылки на rvalue — это будет означать, что вы можете написать:
std::list<C> make_list_somehow()
{
std::list<C> result;
// blah...
return result;
}
И затем назовите это так:
std::list<C> l(make_list_somehow());
Поскольку std::list будет иметь «конструктор перемещения» (подобный конструктору копирования, но выбираемый, когда аргумент является временным, как здесь), он может украсть содержимое возвращаемого значения, т.е.сделать то же самое, что и оптимальный swap
.Таким образом, нет копирования всего списка.(Вот почему C++0x заставит наивно написанный существующий код работать быстрее — многие популярные, но уродливые трюки с производительностью устареют).
И вы можете получить то же самое бесплатно для ЛЮБОГО существующего класса без необходимости писать правильный конструктор перемещения, используя unique_ptr
.
std::unique_ptr<MyThing> myThing(make_my_thing_somehow());
Другие советы
Вы не можете использовать auto в аргументах функции, если вы хотите, чтобы были выведены типы аргументов.Для этого вы используете шаблоны.Посмотри на:http://thenewcpp.wordpress.com/2011/10/18/the-keyword-auto/ иhttp://thenewcpp.wordpress.com/2011/10/25/decltype-and-declval/.Они оба объясняют, как использовать auto и decltype.Они должны предоставить вам достаточно информации о том, как они используются.В частности, другой ответ о make_instance можно было бы лучше сделать с помощью declval.
Я думаю, что суть, которую Джарра подчеркнул в своем ответе, заключается в том, что эти моменты действительно объясняют, как именно использовать эти вещи.Все равно уточню детали:
Вы не можете этого сделать, есть две ошибки:
auto map(const function<auto (_Ty)>& f) -> FList<decltype(f(_Ty))>*
auto
не может использоваться для аргументов функции.Если вы хотите, чтобы тип функции был выведен, вам следует использовать шаблоны.Второе заключается в том, что decltype
принимает выражение, тогда как _Ty
это тип.Вот как это решить:
template <typename Ret>
auto
map(const function<Ret (_Ty)>& f)
-> FList<decltype(f(declval<_Ty>()))>
{}
Таким образом, нет никакой магии в создании экземпляра типа.