Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using auto and decltype in C++11

I'm trying to learn the currently accepted features of c++11 and I'm having trouble with auto and decltype. As a learning exercise I'm extending the std class list with some generic functions.

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

For the member map I want the return type to be generic depending on what the passed function returns. So for the return type I could do something like this.

auto map(const function<float (_Ty)>& f) -> FList<decltype(f(_Ty))>*

This would also need to remove the float type in the function template.

auto map(const function<auto (_Ty)>& f) -> FList<decltype(f(_Ty))>*

I could use a template class but that makes the use of instances more verbose since i have to specify the return type.

template<class T> FList<T>* map(const function<T (_Ty)>& f)

To sum of my question i'm trying to figure out how to define map without using a template class and still have it generic in the type it returns.

like image 824
gradbot Avatar asked Jul 07 '09 18:07

gradbot


People also ask

What is the difference between auto and decltype?

auto is a keyword in C++11 and later that is used for automatic type deduction. The decltype type specifier yields the type of a specified expression. Unlike auto that deduces types based on values being assigned to the variable, decltype deduces the type from an expression passed to it.

Is it good to use auto in C++?

Here it is very good, as it reduces the use of keyboard, without reducing the readability, as anyone can know the type of objects being created, just by looking at the code.

Can you use auto as a return type in C++?

In C++14, you can just use auto as a return type.


1 Answers

Deriving from std::list or other std:: containers is discouraged.

Write your operations as free functions so they can work on any standard container via iterators.

Do you mean "define map without using a template function"?

You should be able to use the result_type member type of std::function to get the type it returns.

Also it's not necessary for you to specify that the function is passed as a std::function. You could leave it open as any type, and let the compiler join everything up. You only need std::function for runtime polymorphism.

And using new to create raw heap-allocation objects and returning them by pointer is soooo 1992! :)

Your iter function is essentially the same thing as the range-based for loop.

But all that aside... do you mean something like this?

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;
}

This will match anything callable, and will figure out the return type of the function by using decltype.

Note that it requires _Ty to be default constructable. You can get around that by manufacturing an instance:

template <class T>
T make_instance();

No implementation is required because no code is generated that calls it, so the linker has nothing to complain about (thanks to dribeas for pointing this out!)

So the code now becomes:

FList<decltype(f(make_instance<_Ty>()))>*

Or, literally, a list of whatever the type would be you'd get from calling the function f with a reference to an instance of _Ty.

And as a free bonus for accepting, look up rvalue references - these will mean that you can write:

std::list<C> make_list_somehow()
{
    std::list<C> result;
    // blah...
    return result;
}

And then call it like this:

std::list<C> l(make_list_somehow());

Because std::list will have a "move constructor" (like a copy constructor but chosen when the argument is a temporary, like here), it can steal the contents of the return value, i.e. do the same as an optimal swap. So there's no copying of the whole list. (This is why C++0x will make naively-written existing code run faster - many popular but ugly performance tricks will become obsolete).

And you can get the same kind of thing for free for ANY existing class of your own, without having to write a correct move constructor, by using unique_ptr.

std::unique_ptr<MyThing> myThing(make_my_thing_somehow());
like image 184
Daniel Earwicker Avatar answered Sep 19 '22 05:09

Daniel Earwicker