C++程序员应了解的那些事:C++ 之父 Stroustrup 推出“ C++ 核心准则”

        2015年的 C++ 大会 9月20-25日在美国华盛顿州的 Bellevue 举行。C++ 之父 Bjarne Stroustrup(本贾尼·斯特劳斯特卢普) 在本次大会的主题演讲上,宣布了C++ 的核心准则(C++ Core Guidelines)。这份准则旨在帮助程序员更加高效地使用现代的 C++("现代 C++" 指 C++11 和 C++14 以及未来的 C++17),英文文档已提供在这个项目的 GitHub repository

       遵循这些原则,会让你的代码静态类型安全、没有资源泄露,发现如今代码中经常出现的程序逻辑错误。它会跑得更快——让你做出正确的事情。

       这份准则目前处于 0.6 版本,正在不断完善中。Bjarne Stroustrup 和 Herb Stutter 是主要的作者,还有来自其它各方面的的贡献者,比如 CERN 的和 Microsoft 的。项目也欢迎更多的贡献者加入进来。

       除了“ C++ 核心准则”。Stroustrup 还宣布了两个类似的项目,Guideline Support Library 和一个检查工具。在 Standard C++ Foundation 的博客上,有这三个项目的详细介绍。

C++核心准则T.1:使用模板提高代码的抽象水平

T.1: Use templates to raise the level of abstraction of code

T.1:使用模板提高代码的抽象水平

Reason(原因):

Generality. Reuse. Efficiency. Encourages consistent definition of user types.

普遍性。重用。效率。鼓励用户类型的一致性。

Example, bad(反面示例):

Conceptually, the following requirements are wrong because what we want of T is more than just the very low-level concepts of "can be incremented" or "can be added":

概念上,我们希望T不仅限于可以进行增量操作或者可以作为被加数这样非常低水平的概念,因此下面的需求是错误的。

template<typename T>
// requires Incrementable<T>
T sum1(vector<T>& v, T s)
{
    for (auto x : v) s += x;
    return s;
}
template<typename T>
// requires Simple_number<T>
T sum2(vector<T>& v, T s)
{
    for (auto x : v) s = s + x;
    return s;
}

Assuming that Incrementable does not  support+ and Simple_number does not support +=, we have overconstrained implementers of sum1 and sum2. And, in this case, missed an opportunity for a generalization.

假设Incrementable不支持+而且Simple_number不支持+=,我们过分约束了sum1和sum2的实现者。而且,在这种情况下,失去了泛化的机会。

Example(示例):

Assuming that Arithmetic requires both + and +=, we have constrained the user of sum to provide a complete arithmetic type. That is not a minimal requirement, but it gives the implementer of algorithms much needed freedom and ensures that any Arithmetic type can be used for a wide variety of algorithms.

假设算术运算既需要+也需要+=,我们已经要求sum的用户提供完全的算术类型。这不是最小化的需求,但是它为算法的实现者提供了所需的更多自由,而且保证算术类型可以用于多种多样的算法。

For additional generality and reusability, we could also use a more general Container or Range concept instead of committing to only one container, vector.

为了额外的泛用性和重用性,我们也可以使用更通用的容器或范围概念代替特定的容器vector。

Note(注意):

If we define a template to require exactly the operations required for a single implementation of a single algorithm (e.g., requiring just += rather than also = and +) and only those, we have overconstrained maintainers. We aim to minimize requirements on template arguments, but the absolutely minimal requirements of an implementation is rarely a meaningful concept.

如果我们定义了一个要求用于特定算法的特定实现的操作的模板(例如只要求+=而不同时要求=和+)而且只要求这些,我们就过分约束维护者了。我们的目的在于最小化模板参数的需求,但是某一实现的绝对最小需求几乎不会成为有意义的概念

Note(注意):

Templates can be used to express essentially everything (they are Turing complete), but the aim of generic programming (as expressed using templates) is to efficiently generalize operations/algorithms over a set of types with similar semantic properties.

模板可以用于从本质上表达任何东西(它们具备图灵完备性),但是泛型编程的目的(像使用模板表达的那样)是高效概括可以适用于具有相似语义属性一套类型的操作/算法。

Note(注意):

The requires in the comments are uses of concepts. "Concepts" are defined in an ISO Technical Specification: concepts. Concepts are supported in GCC 6.1 and later. Consequently, we comment out uses of concepts in examples; that is, we use them as formalized comments only. If you use GCC 6.1 or later, you can uncomment them.

注释中的需求是concept的用法。“Concepts”被定义在ISO技术规格中。GCC6.1之后的版本都支持Concepts。因此,在例子中我们注释掉相关代码;也就是说,我们只将它们用作标准注释。如果你使用GCC6.1之后的版本,你可以去掉注释符号。

concepts文档链接:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4553.pdf)

Enforcement(实施建议):

  • Flag algorithms with "overly simple" requirements, such as direct use of specific operators without a concept.

  • 标记需求过于简单的算法,例如不用concept而直接使用特定操作符。

  • Do not flag the definition of the "overly simple" concepts themselves; they may simply be building blocks for more useful concepts.

  • 不要标记定义过于简单的concept本身;它们没准只是作为某个更有用的concept的一部分存在的。

 

C++核心准则C.89:保证哈希不会抛出异常

C.89: Make a hash noexcept

C.89:保证哈希不会抛出异常

Reason(原因):

Users of hashed containers use hash indirectly and don't expect simple access to throw. It's a standard-library requirement.

哈希容器的用户间接地使用哈希功能,不希望简单的操作发生异常。这是标准库的要求。

<Example, bad(反面示例)>
template<>
struct hash<My_type> {  // thoroughly bad hash specialization
    using result_type = size_t;
    using argument_type = My_type;
 
    size_t operator() (const My_type & x) const
    {
        size_t xs = x.s.size();
        if (xs < 4) throw Bad_My_type{};    // "Nobody expects the Spanish inquisition!"
        return hash<size_t>()(x.s.size()) ^ trim(x.s);
    }
};
int main()
{
    unordered_map<My_type, int> m;
    My_type mt{ "asdfg" };
    m[mt] = 7;
    cout << m[My_type{ "asdfg" }] << '\n';
}

If you have to define a hash specialization, try simply to let it combine standard library hash specializations with ^ (xor). That tends to work better than "cleverness" for non-specialists.

如果你已经定义了哈希特化,争取简单地实现为通过异或和标准库哈希特化的组合。

 

C++核心准则C.87:小心基类的相等运算符

C.87: Beware of == on base classes

C.87:小心基类的相等运算符

Reason(原因):

It is really hard to write a foolproof and useful == for a hierarchy.

为继承体系写出简单又好用的相等运算符真的很难。

Example, bad(反面示例)
struct B {
    string name;
    int number;
    virtual bool operator==(const B& a) const
    {
         return name == a.name && number == a.number;
    }
};

B's comparison accepts conversions for its second operand, but not its first.

B的相等比较运算符的第二个操作数接受类型转换,但是第一个不行!   this指针被修饰为const

struct D :B {
    char character;
    virtual bool operator==(const D& a) const
    {
        return name == a.name && number == a.number && character == a.character;
    }
    // ...
}; 
B b = ...
D d = ...
b == d;    // compares name and number, ignores d's character
d == b;    // error: no == defined
D d2;
d == d2;   // compares name, number, and character
B& b2 = d2;
b2 == d;   // compares name and number, ignores d2's and d's character

Of course there are ways of making == work in a hierarchy, but the naive approaches do not scale

当然有办法让相等比较运算符在继承体系中动作,但是简单的方法不行。

Note(注意):

This rule applies to all the usual comparison operators: !=, <, <=, >, and >=.

本规则适用于所有的常见比较运算符:!=, <, <=, >, 和 >=。

Enforcement(实施建议):

Flag a virtual operator==(); same for other comparison operators: !=, <, <=, >, and >=.

提示被定义为虚函数的相等比较运算符;其他比较运算符也一样:!=, <, <=, >, 和 >=。

 

C++核心准则C.86:保证==语义遵守操作数规则并不会抛出异常

C.86: Make == symmetric with respect to operand types and noexcept

C.86:保证==语义遵守操作数规则并不会抛出异常

Reason(原因):

Asymmetric treatment of operands is surprising and a source of errors where conversions are possible.== is a fundamental operations and programmers should be able to use it without fear of failure.

操作数的非对称处理会令人诧异,而且当可能发生类型转换时会成为错误的源头。==是一个基础的操作而且程序员应该可以使用它而不必担心失败。

Example(示例)
struct X {
    string name;
    int number;
}; 
bool operator==(const X& a, const X& b) noexcept {
    return a.name == b.name && a.number == b.number;
}
Example, bad(反面示例)
class B {
    string name;
    int number;
    bool operator==(const B& a) const {
        return name == a.name && number == a.number;
    }
    // ...
};

B's comparison accepts conversions for its second operand, but not its first.

B的比较运算符可以接受第二个操作数的类型转换,但无法接受第一个参数的类型转换!

Note(注意):

If a class has a failure state, like double's NaN, there is a temptation to make a comparison against the failure state throw. The alternative is to make two failure states compare equal and any valid state compare false against the failure state.

如果一个类有失败状态,就像双精度数的NaN,就会产生一种诱惑比较抛出异常的失败状态。另外一种选择是将两个失败状态的比较结果视为相等,有效状态和无效状态的比较结果视为不相等。(而不抛出异常,译者注)

Note(注意):

This rule applies to all the usual comparison operators: !=, <, <=, >, and >=.

这条规则同样被适用于通常的比较运算符:!=, <, <=, >, 和 >=.

Enforcement(实施建议):

Flag an operator==() for which the argument types differ; 

same for other comparison operators: !=, <, <=, >, and >=.

如果相等运算符的参数是其他类型,进行提示。其他的比较运算符也一样:!=, <, <=, >, and >=。

Flag member operator==()s; same for other comparison operators: !=, <, <=, >, and >=.

标记成员函数比较运算符,其他的比较运算符也一样:!=, <, <=, >, and >=。

 

C++核心准则C.84:swap函数不应该失败

C.84: A swap function may not fail

C.84: swap函数不应该失败

Reason(原因):

swap is widely used in ways that are assumed never to fail and programs cannot easily be written to work correctly in the presence of a failing swap. The standard-library containers and algorithms will not work correctly if a swap of an element type fails.

swap函数被广泛地使用的方式就是假设它永远不会失败,而且也很难写出即使swap出错也能正常动作的程序。标准库容器和算法在元素交换失败时也无法正常工作。

Example, bad(反面示例)
void swap(My_vector& x, My_vector& y)
{
   auto tmp = x;   // copy elements
   x = y;
   y = tmp;
}

This is not just slow, but if a memory allocation occurs for the elements in tmp, this swap may throw and would make STL algorithms fail if used with them.

这段代码的问题不仅是慢,而且如果因为tmp的元素发生了内存申请,如果使用它的话,这个swap可能抛出异常并令STL算法失败。

Enforcement(实施建议):

(Simple) When a class has a swap member function, it should be declared noexcept.

(简单)如果类包含swap成员函数,它应该被声明为noexcept。

 

C++核心准则C.83:对于值类类型,考虑提供一个不会抛出异常的交换函数

C.83: For value-like types, consider providing a noexcept swap function

C.83:对于值类类型,考虑提供一个不会抛出异常的交换函数

Reason(原因):

 A swap can be handy for implementing a number of idioms, from smoothly moving objects around to implementing assignment easily to providing a guaranteed commit function that enables strongly error-safe  calling code. Consider using swap to implement copy assignment in terms of copy construction. See also destructors, deallocation,

and swap must never fail.

交换功能可以在实现很多常规操作时提供便利。从顺畅地移动对象到更容易地实现赋值,以至提供有保证的提交函数,这个函数可以为不会失败的调用代码提供强有力的支持。

Example, good(示例)
class Foo {
public:
    void swap(Foo& rhs) noexcept
    {
        m1.swap(rhs.m1);
        std::swap(m2, rhs.m2);
    }
private:
    Bar m1;
    int m2;
};

Providing a nonmember swap function in the same namespace as your type for callers' convenience

为了调用者的方便,在和目标类型同一个命名空间中提供一个非成员的swap函数。

void swap(Foo& a, Foo& b)
{
    a.swap(b);
}

Enforcement(实施建议):

(Simple) A class without virtual functions should have a swap member function declared.

(简单)不包含虚函数的类就应该定义一个swap函数

(Simple) When a class has a swap member function, it should be declared noexcept.

(简单)如果一个类包含一个swap成员函数,这个函数应该被声明为noexcept。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值