Search code examples
c++c++11constructorinitialization-list

C++ calling function in constructor delegate


In c++11, constructor can be forwarded to another constructor in the initialization list.

It is fine to call function in initialization list as in this question

Is it also fine to call function in the constructor delegate?

I tried code like this:

#include <iostream>
#include <string>
#include <yaml-cpp/yaml.h>

using namespace std;

YAML::Node ParseFromFile(const string& filepath) {
  YAML::Node yaml;
  try {
    return YAML::LoadFile(filepath);
  } catch(const YAML::BadFile& e) {
    cout << "error";
  }
}

class A {
 public:
  A(YAML::Node yaml) {
    cout  << "Got" << endl;
  }
  A(const string& filepath) : A(ParseFromFile(filepath)) {}
};


int main(int argc, char** argv) {
  A a(string(argv[1]));
  YAML::Node yaml = ParseFromFile(string(argv[1]));
  A b(yaml);
  return 0;
}

For the above code, just pass an empty file to it, it will only print one "Got" during the initialization of b.

=======================================================================

Replacing string(argv[1]) with argv[1] makes it work, any ideas why?


Solution

  • Answer to the edited question


    The problem is the first line in the main it's treated as a function declaration and not as a variable initialization, in fact have you compiled it with clang it would've give you a warning about this:

    warning: parentheses were disambiguated as a function declaration

    This is due what is defined in § 6.8 Ambiguity resolution in the standard (AKA Most vexing parse) (emphasis mine... ):

    There is an ambiguity in the grammar involving expression-statements and declarations: An expression-statement with a function-style explicit type conversion (5.2.3) as its leftmost subexpression can be indistinguishable from a declaration where the first declarator starts with a (. In those cases the statement is a declaration.

    Consider the following example:

    #include <iostream>
    #include <string>
    #include <exception>
    using namespace std;
    
    struct A{
        string a;
        A(string s) : a(s){ cout<< "Ctor" <<endl;}
        A(int i) : A(to_string(i)){ }
    };
    
    void foo (A (*a)(string*)){
        cout<<"yay"<<endl;
    }
    
    int main(int argc, char** argv) {
        A         a1(     string(argv[1]) ); // function A a1(std::string*) declaration not a variable 
    /*  ^         ^       ^
        |         |       |
    return type   name   arg type 
    Same as foo
    */
        // foo(a1);//  <- compiles but fails only due to linkage         
        A  a2 = A(string(argv[1])); // works
        A  a3{string(argv[1])}; // works
        A  a4(string{argv[1]}); // works
        A         a5(     ( string(argv[1]) ) ); // works allso but confusing imho
    /*                    ^                 ^
    notice extra paren.   |                 |
    */  
        return 0;
    }
    

    Answer to the original question FWIW


    Why not just try?

    There should be no problem unless you call a function that will use members that are still uninitialized.

    E.g (demo):

    #include <iostream>
    #include <string>
    using namespace std;
    
    class A {
     public:
      A(string x) {
        cout  << "Got "  << x << endl;
      }
      A(int x) : A(std::to_string(x)) {}
    };
    
    
    int main() {
        A a(15);
        return 0;
    }