Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

c++ anonymous constructor doing weird things

This sample program shows how a different constructor will be called depending on whether you pass in a local variable, a global variable, or an anonymous variable. What is going on here?

std::string globalStr;
class aClass{
public:
  aClass(std::string s){
    std::cout << "1-arg constructor" << std::endl;
  }
  aClass(){
    std::cout << "default constructor" << std::endl;
  }
  void puke(){
    std::cout << "puke" << std::endl;
  }
};

int main(int argc, char ** argv){
  std::string localStr;
  //aClass(localStr); //this line does not compile
  aClass(globalStr);  //prints "default constructor"
  aClass(""); //prints "1-arg constructor"
  aClass(std::string("")); //also prints "1-arg constructor"
  globalStr.puke(); //compiles, even though std::string cant puke.
}

Given that I can call globalStr.puke(), I'm guessing that by calling aClass(globalStr);, it is creating a local variable named globalStr of type aClass that is being used instead of the global globalStr. Calling aClass(localStr); tries to do the same thing, but fails to compile because localStr is already declared as a std::string. Is it possible to create an anonymous instance of a class by calling its 1-arg constructor with a non-constant expression? Who decided that type(variableName); should be an acceptable way to define a variable named variableName?

like image 902
Eric Fitting Avatar asked Feb 02 '16 22:02

Eric Fitting


1 Answers

aClass(localStr); //this line does not compile

This tries to declare a variable of type aClass named localStr. The syntax is terrible, I agree, but it's way too late for that [changing the standard] now.

aClass(globalStr);  //prints "default constructor"

This declares one called globalStr. This globalStr variable hides the global one.

aClass(""); //prints "1-arg constructor"

This creates a temporary object of type aClass.

aClass(std::string("")); //also prints "1-arg constructor"

This also creates a temporary.

globalStr.puke(); //compiles, even though std::string cant puke.

This uses the globalStr in main, which is consistent with every other instance of shadowing.

Is it possible to create an anonymous instance of a class by calling its 1-arg constructor with a non-constant expression?

Yes, I can think of four ways:

aClass{localStr}; // C++11 list-initialization, often called "uniform initialization"
(void)aClass(localStr); // The regular "discard this result" syntax from C.
void(aClass(localStr)); // Another way of writing the second line with C++.
(aClass(localStr)); // The parentheses prevent this from being a valid declaration.

As a side note, this syntax can often be the cause of the Most Vexing Parse. For example, the following declares a function foo that returns aClass, with one parameter localStr of type std::string:

aClass foo(std::string(localStr));

Indeed it's the same rule that's responsible for your problems - If something can be parsed as a valid declaration, it must be. That is why aClass(localStr); is a declaration and not a statement consisting of a lone expression.

like image 95
chris Avatar answered Nov 12 '22 11:11

chris