开发者

Evaluation of type for auto in C++0X

I am playing around with the auto feature in the C++0X standard but I am confused how the decision of the type is made. Consider the following code.

struct Base
{
    virtual void f()
    {
        std::cout << "Base::f" << std::endl;
    }
};

struct Derived : public Base
{
    virtual void f()
    {
        std::cout << "Derived::f" << std::endl;
    }
};

int main()
{
    Base* dp = new Derived;
    auto b1 = *dp;
    auto& b2 = *dp;
    std::cout << typeid(b1).name() << std::endl;
    std::cout << typeid(b2).name() << std::endl;
}

It will print Base and Derived.

But why is the auto&evaluated to a ref to Derived and not开发者_开发问答 to a ref to Base?

Even worse changing the code to this:

struct Base{};
struct Derived : public Base{};

int main()
{
    Base* dp = new Derived;
    auto b1 = *dp;
    auto& b2 = *dp;
    std::cout << typeid(b1).name() << std::endl;
    std::cout << typeid(b2).name() << std::endl;
}

returns Base for both types. So why is the type depended on the virtual functions? The compiler I am using is VS2010. Can anyone give me a hint where I can find the definition of this behavior in the standard?


auto in both contexts is yielding Base, not derived. In the first case you are slicing the object (copy at the parent level) while in the second case, because it is a reference, you get a Base& to the actual Derived object. That means that all virtual function calls will be dispatched to the final overrider at the Derived level.

The typeid operator has different behavior for polymorphic types than for non-polymorphic ones. If applied to a reference to a polymorphic type it will perform a type check at runtime and yield the type of the actual object. If it is applied to an object or to a reference to a non-polymorphic type it will be resolved at compile time to the static type of the object or reference.

To verify what the auto is inferring, you can use a slightly different test:

void test( Base& ) { std::cout << "Base" << std::endl; }
void test( Derived& ) { std::cout << "Derived" << std::endl; }

Then call the function and see what type it is resolving to. I would expect the compiler to pick the first overload, as auto& a = *dp; should be equivalent to Base& a = *dp;


In the first case with virtual function(s):

auto b1 = *dp;
auto& b2 = *dp;

First line causes object slicing which means b1 is an object of typeBase, so it prints Base. And the second line is creating an object of type Base& which is a reference to the actual object pointed to by dp. The actual object is of type Derived. Hence it prints Derived.

The second line is equivalent to the following:

Base & b = *dp; //this is also a reference to the actual object
std::cout << typeid(b).name() << std::endl;

What will it print? Base? No. This will print Derived. See this yourself:

  • http://www.ideone.com/9IjPc

Now the second case : when you don't have virtual function in Base then dp points to subobject of object created with new

Base* dp = new Derived; //dp gets subobject

That is why you get Base even with auto & because dp is not a polymorphic object anymore.


The information for the typeid comes from the vtable -- so it uses the actual type of the object, not what the auto might be. In the b1 case, dp got sliced down to a Base, so the vtable is now a Base one. In b2's case, there was no slicing, so it's vtable is the original.

For the second question, RTTI is only available for polymorphic classes (at least one virtual method). This is because the information is only kept if you have a vtable -- allowing simple Plain-old-data objects to be the right size.

http://en.wikipedia.org/wiki/Run-time_type_information


It isn't a reference to derived, it's a reference to Base. When you have a polymorphic object, then typeid() effectively becomes a virtual call and returns the most derived type. The reason that your second test set returns base both times is because there are no virtual functions, so typeid() returns the static type of the object, which is Base.

In other words, it doesn't become a reference to derived, you're just printing out the name wrong. You would have to do typeid(decltype(b2)).name() to be sure that you're looking at the name of the static type of the object.


auto works exactly (but see below) as if you take the whole auto'ed variable type and use it as the type of a function template parameter type. All auto occurrences in the type are replaced by a template parameter. So if you used auto& as the auto'ed variable type, the function template's parameter type becomes

template<typename T>
void f(T&); // auto& -> T&

Now let the variable initializer be the argument in a call f(initializer). The type of the function parameter you get will be the type of the auto'ed variable. For example if you use auto& as the type like above, then f will become a function template with parameter type T&. If the initializer is a Base expression, then the variable ends up having type Base&, because that will be the deduced parameter type for the call f(a_Base_argument).

So, using auto as the auto'ed variable type will use a function template with parameter type T behind the scenes. Calling f(a_Base_argument) will end up with the parameter type Base. This is not a reference to another object anymore, but a new Base variable.

The above way to get the auto'ed type is true for the vast majority of cases, but C++0x has a special case for initializers of the form { a, b, c } (i.e with variable initializers that are braced lists). In your case you don't have such an initializer, so I have ignored that special case.

The reason why in one of your cases Derived instead of Base is explained by other answers. To get what you were looking for using typeid, you can first get the declared type of the variable (using decltype), and then pass that as a type to typeid as in

typeid(decltype(b2)).name()
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜