1 auto关键字
在介绍之前,我们先来了解一个新增的关键字auto。
在C++11中,如果编译器能够在变量的声明时,能够推断出其类型;那么,代替之前必须把变量类型放在声明之前的做法,可以直接用auto进行变量的声明:
int x = 4;
也就是说,上面的声明语句可以被下面的语句替代:
auto x = 4;
当然了,这肯定不是auto关键字设计的根本目的。它最大的作用还是和模板及容器配合使用:
vector<int> vec;
auto itr = vec.iterator();
// 代替vector<int>::iterator itr
在其它的地方,auto也能购派上用场。例如,下面这个例子:
template <typename BuiltType, typename Builder>
void makeAndProcessObject (const Builder& builder)
{
BuiltType val = builder.makeObject();
// do stuff with val
}
这段代码中,有两个必须的模板参数,一个作为对象“builder“的类型;另一个作为对象被构建的类型。更糟糕的是,构建对象的类型不能被模板参数推导出。每一个调用都必须像:
MyObjBuilder builder;
makeAndProcessObject<MyObj>( builder );
但是,auto能够减少这种丑陋,因为你不需要在构建对象的时候,写上指定的类型。而这一切,C++替你完成,就像下面这样:
template <typename Builder>
void makeAndProcessObject (const Builder& builder)
{
auto val = builder.makeObject();
// do stuff with val
}
现在,你只需要一个简单的模板参数,当调用这个函数的时候,另一个参数可以非常简单地被推测出:
MyObjBuilder builder;
makeAndProcessObject( builder );
对于调用者来说,这是更好地方法,模板代码在可读性方面也没有损失什么,如果有的话,反而更容易理解了。
2 decltype和新return语法的乐趣
好了,到这里,你可能会问,是很强大,但是我想返回builder对象创建的值怎么办?标准委员会的专家们当然也想到了这个问题,他们提供了巧妙的解决方法:类型获取函数decltype()和新的return语法。
int multiply (int x, int y);
C++11的做法:返回值放到函数声明的最后,且替换返回值类型为auto关键字,就像下面这样:
auto multiply (int x, int y) -> int;
So would you want to do this? Let’s look at a simple example where it helps us: a class with an enum declared inside it:
class Person
{
public:
enum PersonType { ADULT, CHILD, SENIOR };
void setPersonType (PersonType person_type);
PersonType getPersonType ();
private:
PersonType _person_type;
};
这儿,我们有一个简单的类,Person;用enum类型数据表示其类型。但是当你定义这个类的方法的时候会发生什么呢?
第一个类方法,没有什么问题,你可以用PersonType枚举型:
void Person::setPersonType (PersonType person_type)
{
_person_type = person_type;
}
第二个方法就有些混乱了。这个看上去相当干净的代码无法编译:
// 编译器不知道PersonType是什么?因为其在Person类外面
PersonType Person::getPersonType ()
{
return _person_type;
}
要想编译通过,你必须这样写:
Person::PersonType Person::getPersonType ()
{
return _person_type;
}
上面的代码使能够正常工作的。但是这却非常容易导致错误,尤其是当引进模板的时候,变得更为混乱。
这时候,就需要引进新的return语法了。把返回值放到函数的最后,你不需要添加类的范围作用符了。在编译器到达返回值的时候,它已经知道函数是Person类的一部分了,所以也就知道了PersonType是什么类型了。
auto Person::getPersonType () -> PersonType
{
return _person_type;
}
好了,尽管很漂亮,它真的能够帮我们排忧解难?在之前,我们没有新语法,遇到问题我们不一样解决了?还没结束呢!让我们再介绍一个新的概念:decltype。
3 decltype
Decltype不是auto的恶魔卵生兄弟。Auto允许我们用一个特定的类型声明一个变量;decltype让我们从一个变量或者表达式中抽取类型。什么意思呢?看下面:
int x = 3;
decltype(x) y = x; // same thing as auto y = x;
既然能够作用于表达式,那么,如下:
decltype( builder.makeObject() )
这就让我们得到了类型不是?结合新语法,我们重新实现一开始引入的模板:
template <typename Builder>
auto makeAndProcessObject (const Builder& builder) -> decltype( builder.makeObject() )
{
auto val = builder.makeObject();
// do stuff with val
return val;
}
可惜的是,这只能和新语法一起使用。旧的return语法是无法得到builder返回类型的。
4 Auto, References, Pointers 和 Const
auto和引用&的使用:
int& foo();
auto bar = foo(); // int& or int?
C++11有一个简短的介绍:auto默认成为引用的返回值。所以,在上面的代码中,是int。但是你可以显式地添加&,强迫成为引用:
int& foo();
auto bar = foo(); // int
auto& baz = foo(); // int&
auto指针:
int* foo();
auto p_bar = foo(); // int*
你也可以显式的指明为指针,如下所示:
int* foo();
auto *p_baz = foo(); // int*
auto和const的使用,结合引用:
int& foo();
const auto& baz = foo(); // const int&
或者,与指针
int* foo();
const int* const_foo();
const auto* p_bar = foo(); // const int*
auto p_bar = const_foo(); // const int*
总的说来,它看上去和普通的C++模板类型接口规则一样,不是吗?
1.5 编译器是否支持?
本文中提到的新特性,GCC 4.4和MSVC 10都支持。当然了,在编译的时候,你是需要指定标准的,在GCC中用-std=c++0x。
如果你使用的是其它编译器,需要查看它对C++11的支持度。