我一直在使用c++ 11标准中提供的新的auto关键字来处理复杂的模板类型,我相信它就是为这个目的设计的。但我也用它来做以下事情:
auto foo = std::make_shared<Foo>();
更令人怀疑的是:
auto foo = bla(); // where bla() return a shared_ptr<Foo>
我还没有看到很多关于这个话题的讨论。auto似乎被滥用了,因为类型通常是一种文档和完整性检查的形式。你在使用auto方面的界限在哪里,这个新功能的推荐用例是什么?
澄清一下:我并不是在寻求哲学观点;我要求标准委员会对这个关键字的预期使用,可能还会对如何在实践中实现预期使用发表评论。
我认为,当第一眼就不知道如何写类型,但表达式右边的类型很明显时,应该使用auto关键字。例如,使用:
my_multi_type::nth_index<2>::type::key_type::composite_key_type::
key_extractor_tuple::tail_type::head_type::result_type
要在boost::multi_index中获取复合键类型,即使您知道它是int。你不能只写int,因为它将来可能会被改变。在这种情况下我会写auto。
因此,如果auto关键字在特定情况下提高了可读性,那么就使用它。当读者很清楚auto所代表的类型时,可以使用auto。
下面是一些例子:
auto foo = std::make_shared<Foo>(); // obvious
auto foo = bla(); // unclear. don't know which type `foo` has
const size_t max_size = 100;
for ( auto x = max_size; x > 0; --x ) // unclear. could lead to the errors
// since max_size is unsigned
std::vector<some_class> v;
for ( auto it = v.begin(); it != v.end(); ++it )
// ok, since I know that `it` has an iterator type
// (don't really care which one in this context)
是的,它可能被过度使用而损害可读性。我建议在这样的情况下使用它:确切的类型很长,或者无法表达,或者对可读性不重要,并且变量的寿命很短。例如,迭代器类型通常很长,不重要,所以auto可以工作:
for(auto i = container.begin(); i != container.end(); ++i);
这里的Auto不会影响可读性。
另一个例子是解析器规则类型,它可能很长很复杂。比较:
auto spaces = space & space & space;
with
r_and_t<r_and_t<r_char_t<char>&, r_char_t<char>&>, r_char_t<char>&> spaces =
space & space & space;
另一方面,当类型是已知的并且是简单的,如果显式地声明它会更好:
int i = foo();
而不是
auto i = foo();
我认为,当第一眼就不知道如何写类型,但表达式右边的类型很明显时,应该使用auto关键字。例如,使用:
my_multi_type::nth_index<2>::type::key_type::composite_key_type::
key_extractor_tuple::tail_type::head_type::result_type
要在boost::multi_index中获取复合键类型,即使您知道它是int。你不能只写int,因为它将来可能会被改变。在这种情况下我会写auto。
因此,如果auto关键字在特定情况下提高了可读性,那么就使用它。当读者很清楚auto所代表的类型时,可以使用auto。
下面是一些例子:
auto foo = std::make_shared<Foo>(); // obvious
auto foo = bla(); // unclear. don't know which type `foo` has
const size_t max_size = 100;
for ( auto x = max_size; x > 0; --x ) // unclear. could lead to the errors
// since max_size is unsigned
std::vector<some_class> v;
for ( auto it = v.begin(); it != v.end(); ++it )
// ok, since I know that `it` has an iterator type
// (don't really care which one in this context)
c++程序的一个主要问题是允许你使用未初始化的变量。这导致了我们讨厌的非确定性程序行为。应该注意的是,现代编译器现在抛出适当的/message警告消息,如果程序累了使用它。
为了说明这一点,考虑下面的c++程序:
int main() {
int x;
int y = 0;
y += x;
}
如果我使用现代编译器(GCC)编译这个程序,它会给出警告。这样的警告可能并非如此
如果我们使用的是真正复杂的产品代码,这是非常明显的。
main.cpp:在函数'int main()'中:
Main.cpp:4:8:警告:'x'在此函数中未初始化
(-Wuninitialized)
Y += x;
^
=================================================================================
现在如果我们改变我们的程序,使用auto,然后编译,我们得到如下:
int main() {
auto x;
auto y = 0;
y += x;
}
main.cpp:在函数'int main()'中:
Main.cpp:2:10:错误:声明'auto x'没有初始化式
汽车x;
^
对于auto,不可能使用未初始化的变量。如果我们开始使用auto,这是我们可以(免费)获得的主要优势。
c++专家Herb Shutter在他的CppCon14演讲中解释了这个概念和其他伟大的现代c++概念:
回归基本!现代c++风格要点
auto与线性代数库(如Eigen或OpenCV)大量使用的表达式模板结合使用可能非常危险。
auto A = Matrix(...);
auto B = Matrix(...);
auto C = A * B; // C is not a matrix. It is a matrix EXPRESSION.
cout << C; // The expression is evaluated and gives the expected result.
... // <code modifying A or B>
cout << C; // The expression is evaluated AGAIN and gives a DIFFERENT result.
由这类错误引起的bug是调试的一大麻烦。一种可能的补救方法是,如果执意要将auto用于从左到右的声明样式,则显式地将结果转换为预期的类型。
auto C = Matrix(A * B); // The expression is now evaluated immediately.