Home > Enterprise >  C type to hold members that can't be initialized in the constructor
C type to hold members that can't be initialized in the constructor

Time:01-07

I have some members that can't be initialized at construction time of the container class because the information to construct them is not available. At the moment I'm using std::unique_ptr to construct them later when the information becomes available.

The dynamic allocation/indirection is an unnecessary overhead as I know they will be constructed eventually. I could use std::optional, but I feel the semantics of std::optional will make the ones reading the code think the members are somehow optional and they might be missing which is not the case.

Is there any std library type that can hold a type for later construction with better semantics than std::optional?

EDIT: example code

struct Inner
{
    Inner(int someValue)
        : internalValue(someValue)
    {}

    int internalValue;
};

struct Outer
{
    Outer(){/*...*/}

    void createInner(int someValue)
    {
        assert(inner == nullptr);
        inner = std::make_unique<Inner>(someValue);
    }

    // I would like to avoid the dynamic memory allocation here
    std::unique_ptr<Inner> inner;
};

CodePudding user response:

std:::optional is what you are looking for, eg:

#include <optional>

struct Inner
{
    Inner(int someValue)
        : internalValue(someValue)
    {}

    int internalValue;
};

struct Outer
{
    Outer(){/*...*/}

    void createInner(int someValue)
    {
        inner = Inner(someValue);
    }

    std::optional<Inner> inner;
};

If you don't like how optional looks in your code, you can simply define an alias to give it a more suitable name for your situation, eg:

#include <optional>

struct Inner
{
    Inner(int someValue)
        : internalValue(someValue)
    {}

    int internalValue;
};

template<typename T>
using delayed_create = std::optional<T>;

struct Outer
{
    Outer(){/*...*/}

    void createInner(int someValue)
    {
        inner = Inner(someValue);
    }

    delayed_create<Inner> inner;
};

CodePudding user response:

You can just use a union and placement new.

The advantage of this method is that it is universal. With std::optional you will need a C 17 compiler while with this you can build with any C compiler ever known to manhood.

Example:

class Inner {
public: 
    Inner() = delete;
    Inner( int val ) : a(val) {}
private: 
    int a;
};

class Outer {
    Outer() {}
    ~Outer() {
        if ( initialized ) {
            inner.~Inner();
            initialized = false;
        }
    }
    Outer( const Outer& ) = delete; // TBI
    Outer( Outer&& ) = delete;  // TBI
    Outer( const Outer&& ) = delete; // TBI
    void create( int x ) {
        if ( not initialized ) {
            initialized = true;
            new ((void*)&inner) Inner(x);
        }
    }
private: 
    bool initialized = false;
    union {
        Inner inner;
    };
};

Code above not complete. Needs to fill in the copy/move constructors.

  •  Tags:  
  • Related