c++comma-operator

Syntax to execute some code before initializing member class objects


Is there a way to execute some code as a precursor to initializing class members? Take the following code as an example:

void enableCreationOfAObjects();

class AObject
{
public:
    AObject(int i, int j, int k);
}

class BObject
{
    AObject a;
public:
    BObject():a(enableCreationOfAObjects(), 1, 2, 3){}
}

In the above case, I would like the first comma after calling enableCreationOfAObjects() to be treated as the comma operator and the other commas are parameter separation for AObject constructor.

I can understand this as bad coding style and if that is so, please consider this academic as comma operators are not very common in use. Is there any other way to execute code before member initialization when AObject cannot be rewritten?


Solution

  • Yes, you can do it using the comma operator and additional parentheses:

    BObject() : a((enableCreationOfAObjects(), 1), 2, 3) {}
    

    The first parameter is going to execute enableCreationOfAObjects(), and then 1. However, there is the issue that function arguments are indeterminately sequenced, and 2 or 3 might be evaluated prior to enableCreationOfAObjects. For integer literals, this obviously doesn't matter, but it does matter in the general case.

    You can solve this by using an empty base class:

    struct Empty {};
    
    class BObject : Empty
    {
        AObject a;
    public:
        BObject()
          : Empty((enableCreationOfAObjects(), Empty{}))
          , a(1, 2, 3) {}
    };
    

    Data members are initialized in the order of declaration, and base class subobjects are initialized before any data members. Everything is well-defined then.

    Note that this uses an empty base class because empty base classes don't have a unique address, and therefore this solution has zero-overhead (in terms of memory added to BObject). In C++20, a similar solution can be achieved with [[no_unique_address]]:

    class BObject
    {
        [[no_unique_address]] Empty pre_initialization =
           (enableCreationOfAObjects(), Empty{});
        AObject a;
    public:
        BObject() : a(1, 2, 3) {}
    };