有了新标准,就有了新的做事方法,许多方法都比旧方法好,但是旧方法仍然可以。显然,出于向后兼容的原因,新标准并未正式弃用。因此,剩下的问题是:
哪种旧的编码方式绝对不如C ++ 11风格,我们现在可以做什么?
在回答这个问题时,您可以跳过一些明显的事情,例如“使用自动变量”。
auto_ptr
被弃用了。
有了新标准,就有了新的做事方法,许多方法都比旧方法好,但是旧方法仍然可以。显然,出于向后兼容的原因,新标准并未正式弃用。因此,剩下的问题是:
哪种旧的编码方式绝对不如C ++ 11风格,我们现在可以做什么?
在回答这个问题时,您可以跳过一些明显的事情,例如“使用自动变量”。
auto_ptr
被弃用了。
Answers:
final
防止类派生的说明符std::auto_ptr
由于对右值引用的一流支持,不再需要神奇的工作方式。shrink_to_fit()
成员函数,这应该消除与临时交换的需要。= delete
语法是一种明确表示拒绝特定功能的更为直接的方法。这适用于防止堆分配(即,=delete
对于member operator new
),防止复制,分配等。result_of
:类模板的使用result_of
应替换为decltype
。我想result_of
使用decltype
时,可用它。NULL
应重新定义为nullptr
,但是请参阅STL的演讲以了解为什么他们反对它。我想我会停在那里!
result_of
从清单中删除。尽管typename
之前需要进行繁琐的工作,但我认为typename result_of<F(Args...)::type
有时它比阅读起来更容易decltype(std::declval<F>()(std::declval<Args>()...)
,并且随着N3436被工作文件接受,他们俩都为SFINAE工作(以前没有decltype
这样result_of
做的优点)
在某个时间点,有人争论说,应该按const
价值而不是仅仅按价值返回:
const A foo();
^^^^^
这在C ++ 98/03中几乎没有害处,甚至可能捕获了一些看起来像这样的错误:
foo() = a;
但是const
在C ++ 11中禁止使用by返回,因为它禁止移动语义:
A a = foo(); // foo will copy into a instead of move into it
因此,放松并编写代码:
A foo(); // return by non-const value
A& operator=(A o)&
而不是A& operator=(A o)
。这些可以防止愚蠢的错误,并使类的行为更像基本类型,并且不会阻止移动语义。
只要您可以放弃0
并NULL
赞成,那就nullptr
这样做吧!
在非泛型代码中,使用0
或NULL
无关紧要。但是,一旦您开始在通用代码中传递空指针常量,情况就会迅速改变。当传递0
给a时,template<class T> func(T)
T
将其推导为an int
而不是空指针常量。此后不能将其转换回空指针常量。如果仅使用宇宙,这就变成了根本不存在的问题的泥潭nullptr
。
C ++ 11不会弃用0
并且NULL
为空指针常量。但是您应该像这样做一样进行编码。
std::nullptr_t
。
0
或NULL
用于空指针”)。
安全布尔成语 → explicit operator bool()
。
私有副本构造函数(boost :: noncopyable)→ X(const X&) = delete
用私有析构函数和虚拟继承模拟最终类 →class X final
可以避免使用C ++ 11编写基本算法的一件事是,lambda的可用性与标准库提供的算法结合使用。
我现在正在使用这些,令人难以置信的是,您多频繁地使用count_if(),for_each()或其他算法来告诉您要做什么,而不必再次编写该死的循环。
一旦将C ++ 11编译器与完整的C ++ 11标准库一起使用,您就不再有理由不使用标准算法来构建自己的。Lambda只是杀死它。
为什么?
在实践中(在自己使用这种编写算法的方式之后),阅读用简单的单词(即已完成的工作)构建的内容要比使用某些必须解密才能知道其含义的循环要容易得多。就是说,自动推导lambda参数将大大有助于使语法更容易与原始循环进行比较。
基本上,用标准算法制作的读取算法要容易得多,因为单词会隐藏循环的实现细节。
我猜现在我们只需要考虑较高级别的算法,因为我们可以使用较低级别的算法。
for_each
使用lambda不会比等效的基于范围的for循环更好,因为lambda的内容在循环中。该代码看起来大致相同,但是lambda引入了一些额外的标点符号。您可以使用类似的功能,例如boost::irange
将其应用于更多的循环,而不仅仅是显然使用迭代器的循环。另外,基于范围的for循环具有更大的灵活性,因为您可以根据需要(通过return
或通过break
)提早退出,而for_each
需要抛出。
for
使通常的it = c.begin(), const end = c.end(); it != end; ++it
习惯用法已失效。
for_each
算法在基于for循环的范围内的优势之一是您不能 break
或return
。就是说,当您看到for_each
而无需看身体便立即知道没有这种棘手的问题。
std::for_each(v.begin(), v.end(), [](int &i) { ++i; });
用for (auto &i : v) { ++i; }
。我接受灵活性具有双重优势(goto
非常灵活,这就是问题所在)。我不认为不能够使用的限制break
在for_each
版本补偿了它需要额外的冗长-用户for_each
在这里被IMO牺牲可读性实际和方便的一种理论概念的for_each
是在原则更清晰,概念更简单。实际上,这并不清晰或更简单。
您将需要swap
较少地实现自定义版本。在C ++ 03中,swap
通常必须进行有效的非抛出操作,以避免代价高昂的抛出操作,并且由于std::swap
使用了两个副本,因此swap
通常必须进行自定义。在C ++中,std::swap
使用move
,因此重点转移到实现高效且不抛出异常的move构造函数和move赋值运算符上。由于对于这些默认值通常很好,因此与C ++ 03相比,它的工作量要少得多。
通常,很难预测将使用哪些惯用语,因为它们是根据经验创建的。我们可能会在明年看到“有效的C ++ 11”,而在三年之内只能得到“ C ++ 11编码标准”,因为还没有必要的经验。
我不知道它的名字,但是C ++ 03代码经常使用以下构造代替丢失的移动分配:
std::map<Big, Bigger> createBigMap(); // returns by value
void example ()
{
std::map<Big, Bigger> map;
// ... some code using map
createBigMap().swap(map); // cheap swap
}
这样避免了由于复制省略而导致的任何复制swap
。
map
无论如何复制省略都会构造返回值。您展示的技术如果map
已经存在,将很有用,而不仅仅是被构造。这个例子会更好没有“廉价默认构造函数”的评论,并以“// ...”那个结构和交换之间
当我注意到使用C ++ 11标准的编译器不再错误以下代码时:
std::vector<std::vector<int>> a;
因为据说包含运算符>>,我开始跳舞。在早期版本中,必须要做
std::vector<std::vector<int> > a;
更糟的是,如果您不得不调试它,那么您就会知道由此产生的错误消息有多可怕。
但是,我不知道这是否对您“显而易见”。
按值返回不再是问题。使用移动语义和/或返回值优化(取决于编译器),编码功能更加自然,而没有开销或成本(大部分时间)。