'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 best solutions below

4
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
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
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
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.