'new auto' in C++ constructor

2.7k views Asked by At

Can C++ somehow accept this use of 'auto'?:

class A {
    public:
        A(): m_member(new auto)
        {
                [...]
        }

    private:
        BoringToTypeType *m_member;
}

The purpose is to take advantage of 'auto' by simplifying the member element initialisation in A's constructor. As it is, the code raises the following error:

new expression for type 'auto' requires a constructor argument.
4

There are 4 answers

4
Vittorio Romeo On BEST ANSWER

new auto(...) deduces the type of the resultant pointer from the expression passed inside (...). In your particular case there's nothing that can be deduced.

You have a few options:

  • m_member(new auto(x)), where x is an expression of type BoringToTypeType.

  • m_member(new std::remove_pointer_t<decltype(m_member)>), which is most certainly not an improvement over BoringToTypeType.

If you don't mind defining an additional helper function, here's an alternative solution:

template <typename T, typename... TArgs>
auto newer(T*, TArgs&&... args)
{
    return new T(std::forward<TArgs>(args)...);
} 

class A 
{
    public:
        A(): m_member(newer(m_member, 12))
        {

        }

    private:
        int *m_member;
};

In this case T is used purely for type deduction purposes. m_member has to be repeated twice, but you avoid typing out its type this way.

Simple tests on godbolt.org show that newer does not incur any additional overhead.

0
PiotrNycz On

You might define auto constructor class - the idea it to have new object created when casting needed to pointer to this object (working demo):

First this auto-constructor (or maybe any-constructor) class:

#include <tuple>
#include <utility>

template <typename ...T>
class AutoConstructor
{
public:
    template <typename ...U>
    AutoConstructor(U&&... a) : params(std::forward<U>(a)...) {}

    template <typename U>
    operator U* ()
    {
        return construct<U>(std::index_sequence_for<T...>{});
    }
private:
    template <typename U, std::size_t ...I>
    U* construct(std::index_sequence<I...>)
    {
        return new U(std::forward<T>(std::get<I>(params))...);
    }
    std::tuple<T...> params;
};

It would be hard to use this class w/o helper make function:

template <typename ...T>
auto makeAuto(T&&... a)
{
   return AutoConstructor<T...>(std::forward<T>(a)...);
}

Then - such "magic" is possible:

long&& d = 7;
int* a = makeAuto();
S *s = makeAuto(std::ref(*a), 2, std::make_unique<float>(3), std::move(d));

Assuming S has c-tor S(int& a, const int& b, std::unique_ptr<float> c, long d)

Just to summarize - this piece of code I presented is quite easy to use - but I doubt it would be good habit to have everywhere in the code makeAuto calls...

0
Martin Bonner supports Monica On

Make m_member a std::unique_ptr, and you will get access to element_type. So the code looks like:

class A {
    public:
        A(): m_member(std::make_unique<decltype(m_member)::element_type>(...))
        {
                [...]
        }

    private:
        std::unique_ptr<BoringToTypeType> m_member;
};

It's debatable whether that's an improvement - but it helps if BoringToTypeType is more complex.

0
TartanLlama On

If you don't want to repeat that type name, you can add a private alias:

class A {
    using mem_t = BoringToTypeType;
    public:
        A(): m_member(new mem_t)
        {
                [...]
        }

    private:
        mem_t *m_member;
}

Of course, in real code you should use std::unique_ptr or similar instead of raw pointers in most cases.