1

Recently I've bumped into a weird behavior when having a class with a template constructor that accepts only one argument. This class under certain circumstances fails to initialize at all.

#include <iostream>

struct Foo
{
    template<typename T>
    Foo(const T& x)
    {
        std::cout << "--" << x << std::endl;
    }
};

int main(int, char**)
{
    Foo f(std::string());

    std::cout << "++" << f << std::endl;
    return 0;
}

What I have is a Foo structure with the constructor you see. In main I create a new Foo instance with an empty std::string. The example compiles and the output is:

++1

WTF??? As you may have noticed there are three problems with the this. First of all the constructor was never called, secondly the f was printed as "1", thirdly there is no overloaded operator<< that prints the Foo using the cout and despite that the compiler never complained. This means that the f is not a type of Foo.

I change the example slightly:

int main(int, char**)
{
    Foo f(std::string(""));

    std::cout << "++" << f << std::endl;
    return 0;
}

...and the compiler throws an error that there is no overloaded operator<< and that is normal.

int main(int, char**)
{
    Foo f(std::string("Hello"));
    return 0;
}

Now the output is:

--Hello

and that's normal again.

The problem is when having passing to Foo::Foo a an empty object whether it is std::string() or float() or any_type(). I've tested the example in GCC 4.4 and 4.6 and I find this behavior very unexpected.

What is your opinion? Is this a GCC bug or am I missing something here?

2 Answers2

5
Foo f(std::string());

It does not create an instance of type Foo, as you seem to think.

Instead, this declares a function of name f, whose return type is Foo, and parameter type is a function type (which takes no argument and returns std::string).

It is same as:

Foo f(std::string (*)()); //same as yours

The only difference this and your function declaration is that in your case the parameter type is function type (which eventually decays into function pointer type anyway), and in my case, the parameter type is function pointer type (which doesn't need to decay into pointer type).

As for why it prints ++1 is because it invokes operator<< which takes void* as argument, and this function prints the address of the function.

Now if you want to declare an object, then you need to use extra parens as:

Foo f((std::string())); //notice the extra parens!

This declares an object, and calls the constructor : http://ideone.com/Px815


See also this topic:

Community
  • 1
  • 1
Nawaz
  • 353,942
  • 115
  • 666
  • 851
2

You're experiencing the infamous C++ Most vexing parse. In your first example

 Foo f(std::string());

is parsed as a function declaration, so no constructor is called.

Paolo Capriotti
  • 4,052
  • 21
  • 25