Auto Type Deduction At A Glance

One of the new features in C++0x is auto type deduction. Two keywords are introduced in the new standard – auto and decltype.

This feature allows the type of a variable to be deduced from its initializer. Here is an example straight from n1984.

// x has type double, not sure why I would do this though.
auto x = 3.14;
// y has type of x, which is also double. Now this is just confusing.
decltype(x) y = 3.14;

Simple concept, yet confusing. Yup, sounds like C++ alright :).

Since I have GCC 4.4 installed on Ubuntu, I spent some time playing around to get a feel for it.

Reducing Verbosity

C++ is a statically-typed language. Therefore, it is more strongly typed than other interpreted language like Ruby or Python. Types in C++, especially with generic programming, are often defined within the scope of another class or namespace. This often becomes very verbose.

std::vector<int> v(100,0);
std::vector<int>::iterator vi = v.begin(); // very verbose
std::vector<int>::iterator vend = v.end(); // too much typing
while(vi != vend)
{
   std::cout <<*vi << std::endl;
   ++vi;
}

With the keyword auto, declaration becomes simple.

std::vector<int> v(100,0);
auto vi = v.begin(), vend = v.end(); // multi-declarator from n1737
while(vi != vend)
{
   std::cout <<*vi << std::endl;
   ++vi;
}

Auto May Fail in RTTI

I am convinced that every feature in C++ comes with bullets to blow your legs away, and this is no exception.

The compiler deduces the type of auto during compile time, but what about run-time? Well, it might not work very well, depending on your scenario.

Here’s is an example that can cause problems.

class Base
{
public:
	virtual void f() {};
};
class Derived : public Base
{
public:
	virtual void f() {};
};
void Oops()
{
	Base * d = new Derived;
	// ...
	auto b = *d; // b has type Base, therefore unintentional slicing occurs
	b.f(); // calls the wrong function
}

Imposter Problem

When using the auto keyword, there is another interesting side effect that might lead to undesired results.

Consider this example where a library exports Object and CreateObject() and it is included by another program. The compiler will deduce the type of variable o to be Object.

// including an external library
class Object { public:	void f(){}; }
Object CreateObject() { return Object(); }
...
// user code
void DoStuff()
{
	auto o = CreateObject(); // o should be Object
	o.DoStuff();
}

If a programmer accidentally compiled against another library that exports similar interfaces, the user code will compile without any warnings or errors.

// oops, included the wrong library
class Imposter { public: void f(){}; };
Imposter CreateObject() { return Imposter(); }
...
// compiler is perfectly happy in user code
void doStuff()
{
	auto o = CreateObject(); // o is now Imposter
	o.DoStuff();
}

Some might argue that this is a “feature”. I disagree because if I want this behavior, I would’ve made DoStuff() a generic function that takes in decltype(CreateObject()) (more on that later).

What we have here is a non-generic function having generic behavior. I do not feel comfortable using auto in this case.

Trailing-Return Type

C++0x allows for an alternative syntax to declare functions, with trailing-return-type.

Here is an example for a function that returns an integer.

auto func() -> int
{
	return 5;
}

The example here seems silly because this syntax is really designed for generic programming.

Now consider another example where a generic function GenericMerge() merges two classes into a third class.

class A{}; class B{}; class C{};
// merge function that create C out of A and B
C Merge(A const &a, B const &b ) { return C(); }
// a generic function that just merge all types
template<typename Lhs, typename Rhs>
auto GenericMerge(Lhs const &lhs, Rhs const &rhs)
	-> decltype(Merge(lhs, rhs))
{
	return Merge(lhs, rhs);
}

In C++03, this would not have been possible because the compiler does not know that C is the result of Merge(). With trailing-return-type, the return type is deferred as type of Merge(lhs,rhs).

This is a very powerful upgrade to generic programming in C++.

sidenote: Expressions in decltype will not be invoked at runtime.

Conclusion

auto will help to reduce the verbosity of the code. At the same time, it may lead to more confusing code.

Like all C++ features, auto comes with bullets to blow your legs away.

Trailing-return-type gives a serious power-up to generic programming.

Tools

GCC 4.4 with Eclipse, Ubuntu 9.10

Leave a comment