What the =?

February 10th, 2009 | Tags:
#include <iostream>
using namespace std;
 
struct Foo {
    Foo() {
        cout<<"Default constructor."<<endl;
    }
    Foo(Foo const&) {
        cout<<"Copy constructor."<<endl;
    }
    Foo(int) {
        cout<<"Conversion constructor."<<endl;
    }
    Foo& operator = (int) {
        cout<<"Assignment operator."<<endl;
        return *this;
    }
};
 
int main() {
    Foo a(1);
    Foo b = 1;
    Foo c = Foo(1);
    return 0;
}

Do you know what this program will output?

I have the impression that most C++ programmers will tell you that the first will invoke the conversion constructor, but will be split on whether the second and third forms call the conversion constructor and the assignment operator, or the conversion constructor and the copy constructor.  This means that I have the impression that most C++ programmers don’t know what these statements really mean to the C++ compiler.

The Form Type t = expr;

The expression “Foo b = 1;” is an alternate way of saying “Foo b(1);”.  Both are considered forms of direct initialization by the C++ language specification, so no assignment or copy constructor is invoked.  In a statement which declares and initializes a variable, the “=” is not a true assignment operator, but an indication to the compiler of how to initialize the value.

The Form Type t = Type(expr);

In C++, constructors don’t really have names.  The syntax

Foo(1);

is not really considered a function call (where the function happens to be a constructor), it is actually considered to be an explicit type conversion from “1″ to Foo.  This is commonly called a function-style cast and is closer to “int(5.3f)” than a function call. The only difference is that multiple values can be used in a function-style cast for user-defined types.

If we combine these facts, we can conclude that the statement “Foo c = Foo(1);” only invokes the conversion constructor as well.  The “=” indicates initialization and the “Foo(1)” only casts the type; therefore, again, only the conversion constructor is called.

Other Conversions

Since constructors are invoked for conversions, there are other forms which are simply constructor invocations:

(Foo) 1;
static_cast<Foo>(1);

Both of these could replace “Foo(1)” above without changing the meaning of the statement.

  1. February 11th, 2009 at 19:56
    Reply | Quote | #1

    Rule of thumb: If you need a //FIXME post to figure out what the compiler should do in an edge case like this, you’re writing obtuse code and it’s probably unnecessary.

  2. February 11th, 2009 at 21:19
    Reply | Quote | #2

    @Mark W. “catfood” Schumann
    Agreed. I was trying to write for the people discovering obtuse code.

    Although I am sharing this fascination with DSELs that is going around. This seems to be a good excuse to abuse dark corners of your favorite language. But outside of that corner case, just don’t do it.

TOP