3

A template class and a normal class:

template <typename Type>
class Holder
{
public:
    Holder(const Type& value) : held_(value)
    {
        cout << "Holder(const Type& value)" << endl;
    }
    Type& Ref() { return held_; }
private:
    Type held_;
};

class Animal
{
public:
    Animal(const Animal& rhs) { cout << "Animal(const Animal& rhs)" << endl; }
    Animal() { cout << "Animal()" << endl; }
    ~Animal() { cout << "~Animal" << endl; }
    void Print() const { cout << "Animal::Print()" << endl; }
};

Then I want to instantiate a Holder<Animal> with this statement Holder<Animal> a(Animal());, however, it fails. I mean Animal() is not treated as a temporary object. And this statement doesn't call Holder's constructor.

If someone could explain? I'm not clear. I'm guessing a becomes a type here. Then, I use Holder<Animal> a = Holder<Animal>(Animal());, it works well. So, there are some cases here:

  1. Holder<Animal> a(Animal()); a.Ref().Print(); // error
  2. Holder<Animal> a = Holder<Animal>(Animal()); a.Ref().Print(); // ok
  3. Holder<int> b(4); b.Ref() = 10; cout << b.Ref() << endl; //ok

Can explain? I'm just a little confused with the first statement. And the error information this statement causes:

GCC4.7.2: error: request for member 'Ref' in 'a', which is of non-class type 'Holder<Animal>(Animal (*)())'

VS10: error C2228: left of '.Ref' must have class/struct/union, error C2228: left of '.Print' must have class/struct/union

CB Bailey
  • 755,051
  • 104
  • 632
  • 656
Ggicci
  • 785
  • 2
  • 13
  • 29
  • A good question... I don't really know the answer. But won't `Animal()` be treated as a temporary object? Doesn't it go out of scope imemdiately? – Dariusz Sep 03 '13 at 06:32
  • @Dariusz no it would last until the `;` is reached, i.e. until `a`'s constructor is completed. – Arne Mertz Sep 03 '13 at 06:36
  • @ArneMertz ok, by "immediately" i meant after that line's execution is finished, which means after the constructor. So since we take a reference to that object, the reference will become invalid? ... Ah, it won't, holder member `held_` is not a reference, never mind. – Dariusz Sep 03 '13 at 06:38
  • @Dariusz that's the C++03 way to do it - the temporary gets copied and the copy survives. In C++11, the constructor would take the parameter by value and move-construct the member variable from it. That way the temporary `Animal` would get moved twice and end up in the `held_` member variable. – Arne Mertz Sep 03 '13 at 06:47
  • possible duplicate of [Most vexing parse(C++)](http://stackoverflow.com/questions/5926103/most-vexing-parsec) – Joris Timmermans Sep 03 '13 at 07:02

2 Answers2

7

The statement Holder<Animal> a(Animal()); does not create a variable, but declares a function that returns a Holder<Animal> and that takes a function in parameter. It's usually called the most vexing parse, because of this ambiguity (that one would expect a variable rather than a function declaration).

Herb Sutter explains the different possible syntaxes here. In C++11, a possible solution is:

auto a = Holder<Animal> {};
piwi
  • 5,136
  • 2
  • 24
  • 48
3

You are victim to the "most vexing parse":

Holder<Animal> a(Animal()); is parsed as a function with name a, that returns a Holder<Animal> and takes as parameter another function, which has no parameters and returns an Animal.

In C++11 you can solve that problem by using uniform initialization:

Holder<Animal> a{Animal{}};

Arne Mertz
  • 24,171
  • 3
  • 51
  • 90