(Effective Modern C++) – 第二章

Item5:Prefer auto to explicit type declaration

建议使用auto的情况:

  • lambda表达式产生一个可调用的对象,但是与以往的可调用对象不同。不同的lambda表达式产生的可调用对象的类型是不一样的,即使他们的签名一致。一般的做法是使用functional头文件里的std::function函数适配器。但是有个问题就是std::function可能会随着运行而变慢变大。所以最好的办法就是使用auto接受可调用对象的参数。

  • 一般的size_t类型大小在32位的Windows下是32位的(与unsigned相同),但是在64位Windows下是64位的(与unsigned __int64相同)。如果想当然认为unsigned与size_t相同,在64位下就会造成截断的问题。
  • 考虑这个代码
std::unordered_map<std::string, int> m;
…
for (const std::pair<std::string, int>& p : m)
{
  …                   // do something with p
}

遍历m中的pair对时,返回的时std::pair<const KeyType, ValueType>&类型。这个类型与auto声明的类型不同。所以编译期试图创建一个临时的对象以致于我们不能引用m中的对象而是引用的临时对象。for (const auto& p : m)是一个不错的选择

  • auto推导的类型可以随着表达式的值的类型改变而改变。
  • auto&&可以代表cv限定符以及左右值属性。auto&也可以保留cv限定符,并且可以修改原对象。auto只是一个拷贝同时不保留cv限定符

详细的介绍:https://stackoverflow.com/questions/13230480/what-does-auto-tell-us

Things to Remember
• auto variables must be initialized, are generally immune to type mismatches
that can lead to portability or efficiency problems, can ease the process of
refactoring, and typically require less typing than variables with explicitly
specified types.
• auto-typed variables are subject to the pitfalls described in Items 2 and 6.

Item 6: Use the explicitly typed initializer idiom when auto deduces undesired types.

#include <vector>
#include <iostream>
struct Widget
{

};
std::vector<bool> features(const Widget &w)
{
	return std::vector<bool>{false, false, false};
}

void process(bool b)
{
	int u = 444;
}
int main()
{
	Widget w;
	auto highprior = features(w)[1];
	//解决办法
	auto oo = static_cast<bool>(features(w)[1]);
	//process(oo);
	process(highprior);//_DEBUG_ERROR("vector<bool> iterator not dereferencable");
	system("pause");
	return 0;
}

使用auto推导std::vector<boo>的operator[]的返回类型会抛出_DEBUG_ERROR("vector<bool> iterator not dereferencable");的异常

std::vector<bool>是一个特化的vector模板,operator[]返回的是std::vector<bool>::reference这种代理类(作为中间值,模拟一个相同的行为)。而feature返回的右值的vector的代理类指向一个临时的vector的第二个元素的位置。最后临时的vector被销毁而highprior对象仍旧指向一个非法的位置(空悬了)。

解决方案:auto推导得到的代理类型并不是我们需要的,我们需要强制转换为我们所需要的类型,例如bool

遍历时的问题:代理类是引用的容器内的位置,所以for循环中的b是赋值的左值引用。

#include <vector>
#include <iostream>
int main()
{
	std::vector<bool> bools{ false, false, false };

		// change value (expected to see bools are not changed).
		for(auto b : bools)
		{
			b = true;
		}

	// print bools
	for (auto const & b : bools)
	{
		std::cout << std::boolalpha << b << std::endl;
	}

	return 0;
}
Things to Remember
• “Invisible” proxy types can cause auto to deduce the “wrong” type for an ini‐
tializing expression.
• The explicitly typed initializer idiom forces auto to deduce the type you want
it to have.

参考&引用: