2016-11-02 2 views
3

Dies ist eine Frage zur Syntax von C++ - Initialisierungslisten.C++ - Initialisiererlistenfunktionen: Aufruffunktionen ohne initialisierendes Member?

Ist es möglich, Funktionen aus Initialisierungslisten aufzurufen, ohne dass sie Argumente für Konstruktoren von Elementobjekten sind?

Das unten aufgeführte Codebeispiel ist paraphrasiert (paracodiert?) Aus einer ähnlichen Situation bei der Arbeit.

Die Situation

  • eine Elementvariable nimmt einen Zeiger auf einen Singleton als Konstruktor Argument.
  • Die Elementvariable wird von der Initialisierungsliste im Konstruktor der enthaltenden Klasse erstellt.
  • Der Singleton wurde nicht erstellt, bevor die umschließende Klasse erstellt wurde.

Der Kodex

#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; 
} 

Der Compiler-Fehler

>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 

Die Frage

Ist es syntaktisch möglich, eine Funktion von einem cla anrufen SS-Konstruktor Initialisierungsliste ohne ein Argument zu einem Nicht-Standardkonstruktor des Mitgliedsobjekts zu sein?

Die Frage ist für akademische Neugier. Ich kenne mindestens eine andere Lösung, um das Singleton zu instanziieren, bevor die umschließende Klasse erstellt wird.

Antwort

1

Sie können die comma operator verwenden.

In Ihrem Beispiel

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

Beachten Sie die zusätzlichen Klammern um die beiden Ausdrücke, werden diese würden sonst als zwei anstelle von einem Parameter interpretiert.

1

Ist es möglich, Funktionen aus Initialisierungslisten aufzurufen, ohne dass sie Argumente für Konstrukte von Elementobjekten sind?

So etwas funktioniert vielleicht wie beabsichtigt:

void f() {} 

struct S { 
    S(): i{(f(), 0)} {} 
    int i; 
}; 

int main() { 
    S s; 
} 

Die Grundidee auf dem Komma-Operator angewiesen ist. In diesem Fall wird der von der Funktion zurückgegebene Wert (falls vorhanden) verworfen und nicht zum Initialisieren eines Elements verwendet.
Natürlich nutzen wir immer noch die Tatsache, dass ein Datenelement existiert, also ist es vielleicht nicht genau das, wonach Sie suchen.

Wenn Sie ganz von Datenelemente loswerden wollen, können Sie etwas ähnliches mit einem delegierten Konstruktor wie im folgenden Beispiel tun:

void f() {} 

class S { 
    S(int) {} 

public: 
    S(): S{(f(), 0)} {} 
}; 

int main() { 
    S s{}; 
} 

Keine zählt, was der Rückgabetyp der aufgerufenen Funktion ist. Mit dem Komma-Operator wird der Wert int als Tag verwendet, um den Aufruf an den rechten Compiler zu senden, und dann wird er verworfen.

+0

Danke; Ich habe die Antwort von Olaf vor deiner bekommen, aber danke dir auch, dass du mich dem Komma-Operator vorgestellt hast. – StoneThrow