Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ initializer list capabilities: call functions without initializing member?

This is a question on the syntax of C++ initializer lists.

Is it possible to call functions from initializer lists without them being arguments to member object constructors?

Code example listed below is paraphrased (paracoded?) from a similar situation at work.

The Situation

  • A member variable takes a pointer to a singleton as constructor argument.
  • The member variable is constructed by initializer list in its containing class' constructor.
  • The singleton has not been created prior to the containing class being constructed.

The Code

#include <iostream>

#define LOG { std::cout << __PRETTY_FUNCTION__ << std::endl; }

namespace
{

template <class T>
class SingletonService
{
public:
    static T* Instance() { LOG; return mpT; }
    static void InstallInstance(T* pT) { LOG; mpT = pT; }
    static void DeleteInstance() { if (mpT) delete mpT; }

protected:
    static T* mpT;
};

template <class T>
T* SingletonService<T>::mpT = NULL;

class OneOfMe
{
public:
    OneOfMe() { LOG; };
    virtual ~OneOfMe() { };
};

class Container
{
public:
    Container(OneOfMe* pObj) { LOG; /* Do something with pObj */ }
    virtual ~Container() { }
};

int GenerateNum()
{
    return 42;
}

class Baz
{
public:
    Baz(int num) : mNum(num) { LOG; }
    virtual ~Baz() { }
protected:
    int mNum;
};

class Bar
{
public:
    Bar() : mBaz(GenerateNum()) { LOG; } // Perfectly OK to call function that is argument to member object's non-default ctor.
    virtual ~Bar() { };

protected:
    Baz mBaz;
};

class Foo
{
public:
    Foo()
        : SingletonService<OneOfMe>::InstallInstance(new OneOfMe) // Compile error
        , mContainer(SingletonService<OneOfMe>::Instance()) { }
    virtual ~Foo() { };
protected:
    Container mContainer;
};

}

int main(int argc, char* argv[])
{
    LOG;
    Bar bar;

    SingletonService<OneOfMe>::InstallInstance(new OneOfMe);    // This works.
    Container container(SingletonService<OneOfMe>::Instance()); // And this works.
    SingletonService<OneOfMe>::DeleteInstance();
    return 0;
}

The compile error

>g++ main.cpp
main.cpp: In constructor ‘<unnamed>::Foo::Foo()’:
main.cpp:45: error: expected class-name before ‘(’ token
main.cpp:45: error: no matching function for call to
‘<unnamed>::Container::Container()’
main.cpp:37: note: candidates are:
<unnamed>::Container::Container(<unnamed>::OneOfMe*)
main.cpp:35: note:
<unnamed>::Container::Container(const<unnamed>::Container&)
main.cpp:45: error: expected ‘{’ before ‘(’ token

The Question

Is it syntactically possible to call a function from a class constructor's initializer list without being an argument to a member object's non-default constructor?

The question is for academic curiosity. I know at least one other solutions is to instantiate the singleton before creating the containing class.

like image 607
StoneThrow Avatar asked Mar 10 '23 18:03

StoneThrow


1 Answers

You can utilize the comma operator.

In your example

class Foo
{
public:
    Foo()
        : mContainer((SingletonService<OneOfMe>::InstallInstance(new OneOfMe),
                      SingletonService<OneOfMe>::Instance()))
    {}
    virtual ~Foo();
protected:
    Container mContainer;
};

Note the additional parentheses around the two expressions, otherwise these would be interpreted as two instead of one parameter.


Another approach to this particular problem could be to return the singleton from InstallInstance() as well, e.g.

template <class T>
class SingletonService {
public:
    static T *InstallInstance(T *pT) { LOG; return mpT = pT; }
};

and then

class Foo {
public:
    Foo()
        : mContainer(SingletonService<OneOfMe>::InstallInstance(new OneOfMe)) {}
    virtual ~Foo();
protected:
    Container mContainer;
};
like image 166
Olaf Dietsche Avatar answered Apr 29 '23 14:04

Olaf Dietsche