Search code examples
c++ctor-initializer

How can I call two functions in contructor's member initializer list?


Can i call a function in initialize list? please see this code:

#include <string>
using namespace std;

class A {
 public:
  A(string path) : s(cfg.getRoot()) {  // before i call getRoot, i need to call cfg.readFile(path.c_str()), is there any methods? (readFile return void)
  }

  private:
   libconfig::Config cfg;
   const libconfig::Setting & s;  // const &, so initalizer list is the only chance for me to init it
}

Solution

  • You can also make use of the comma operator (although this may or may not be harder to understand for a reader):

    A(string path) : s((cfg.readFile(path.c_str()), cfg.getRoot())) {}
    

    The built-in comma operator evalues from left-to-right and discards the result of the left-hand expression. Note that the double parentheses are necessary. Otherwise it will be parsed as two function arguments to s's constructor.

    This will not work correctly if the return type of cfg.readFile(path.c_str()) has an overloaded comma operator (which is however very rare). In that case you need to cast the result to void in order to discard the return value:

    A(string path) : s((static_cast<void>(cfg.readFile(path.c_str())), cfg.getRoot())) {}
    

    The lambda approach was already given. You can avoid repeating the return type if you use decltype(auto):

    A(string path) : s([this]()->decltype(auto){
        cfg.readFile(path.c_str());
        return cfg.getRoot();
    }()) {}
    

    Again it is a matter of style, whether you prefer giving the return type explicitly or not.

    As noted by @songyuanyao in a comment below, if you do not specify a return type or appropriate placeholder, then you will get into trouble if cfg.getRoot returns a reference, because the default lambda type is an auto placeholder, which will cause contruction of a temporary object from the reference, which you would then bound to s and will immediately be destroyed again.