Вопрос

Я пытаюсь изучить принятые в настоящее время функции С++ 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>()))>
{}

Таким образом, нет никакой магии в создании экземпляра типа.

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