0

I have a class Foo with 2 constructors, A and B.

  • Constructor B contains some important setup code and should always be run when an object is instantiated.

  • At the end of constructor A, I want to execute the important setup code that is inside B.

Below is an example of the described setup which involves connecting to a database.

class Foo {
public:
  // Constructor A
  Foo(std::string db_path) {
    // ... Some input validation and retry logic for opening the database.
    Database db = ...
    // ...
    Foo(db);      // Impossible to directly call another constructor from the body of a constructor.
  }

  // Constructor B
  Foo(Database db)
      : db_(db) {
    // ... Some important setup code.
  }

private:
  Database db_;
};

I believe it isn’t possible to call a constructor from another constructor’s body in C++ like you can in other languages such as C#.

I know that C++11 has the ‘delegating constructors’ feature, however I don’t believe I can make use of this because I have to perform tasks such as input validation and setting up retry logic in the body of constructor A.

One alternative that has been suggested here is to extract out the important setup code in constructor B into a private init method and have both constructors just call that init method.

This works well for my use case, however I am wondering if there is a modern way of accomplishing constructor chaining in C++.

Thanks!

Tymotex
  • 80
  • 1
  • 2
  • 6
  • 1
    A private `init()` function seems reasonable. – Ben Jan 26 '22 at 03:48
  • 1
    I'm puzzled why you think this is not "modern". Even if there was a "modern" way to do it I usually stick with the caveman style - it has been proven in practice. Most of the modern stuff is just syntax sugar that accomplishes exactly the same thing - case in mind: lambdas and functors. –  Jan 26 '22 at 03:53
  • 1
    `I know that C++11 has the ‘delegating constructors’ feature, however I don’t believe I can make use of this because I have to perform tasks such as input validation and setting up retry logic in the body of constructor A.` then you misunderstand what constructor in in C++. Call to the constructor is part of initialization. What you imply that you want deliberately fail or alter initialization. Under certain circumstances initialization may result in no code at all or minimal, "compressed code", which would initialize whole object with its parents. In that way C++11 is more modern than C# – Swift - Friday Pie Jan 26 '22 at 03:53
  • 1
    The disadvantage of an init function is that you cannot use member initializiation lists. But in your case `Database` seems to be a lightweight object, because you assign it by copying to value. To delay initialization in those cases `std::optional` can be used. – Sebastian Jan 26 '22 at 04:51

2 Answers2

4

You could create a private function (or a free function with internal linkage) to initialize the parameters to your delegate constructor. For example:

class Foo {
private:
    Database connect_to_db(const std::string& db_path) {
        // Validation and retry logic
    }

public:
    Foo(const std::string& db_path)
        : Foo{connect_to_db(db_path)}
    {}

    Foo(Database db)
        : db_{db}
    {
        // Important setup code
    }

private:
    Database db_;
};
Miles Budnek
  • 28,216
  • 2
  • 35
  • 52
1

You can call constructors from constructors, like this : (The constructor with string parameter calls the constructor with int parameter)

#include <cassert>
#include <string>

class Foo
{

public:
    explicit Foo(int value) :
        m_value{ value }
    {
    };

    explicit Foo(const std::string& value) :
        Foo(std::stoi(value))
    {
    }

    int value() const
    {
        return m_value;
    }

private:
    int m_value;
};


int main()
{
    Foo f1(42);
    Foo f2("42");

    assert(f1.value() == f2.value());

    return 0;
}
Pepijn Kramer
  • 9,356
  • 2
  • 8
  • 19
  • 1
    While useful info in general, it would be more helpful if you would show this technique used in the context of the OP's code. – Remy Lebeau Jan 26 '22 at 04:39