C++|函數定義、參數傳遞、值返回的核心規則

小智雅匯 發佈 2019-12-31T11:46:11+00:00

X*find; 2 Parameter passing expression rules:參數傳遞表達式規則:2.1 Prefer simple and conventional ways of passing information喜歡簡單和傳統的信息傳遞方式2.2 For "

A function specifies an action or a computation that takes the system from one consistent state to the next. It is the fundamental building block of programs.

函數指定將系統從一個一致狀態轉換到下一個一致狀態的操作或計算。它是程序的基本組成部分。

It should be possible to name a function meaningfully, to specify the requirements of its argument, and clearly state the relationship between the arguments and the result. An implementation is not a specification. Try to think about what a function does as well as about how it does it. Functions are the most critical part in most interfaces.

應該能夠有意義地命名函數,指定其參數的要求,並清楚地說明參數與結果之間的關係。實現不是規範。試著思考一個函數做什麼以及它是如何做的。函數是大多數接口中最關鍵的部分。

Function rule summary:

函數規則摘要:

1 Function definition rules:

函數定義規則:

1.1 "Package" meaningful operations as carefully named functions

包裝有意義的操作作為精心命名的函數

1.2 A function should perform a single logical operation

一個函數應該執行一個邏輯操作

1.3 Keep functions short and simple

保持功能簡潔

1.4 If a function may have to be evaluated at compile time, declare it constexpr

如果函數可能必須在編譯時求值,請將其聲明為constexpr

1.5 If a function is very small and time-critical, declare it inline

如果一個函數非常小且時間是關鍵因素,則將其聲明為內聯

1.6 If your function may not throw, declare it noexcept

如果函數不能拋出異常,則聲明它為noexcept

1.7 For general use, take T* or T& arguments rather than smart pointers

對於一般用途,使用T*或T&a 參數,而不是智能指針

1.8 Prefer pure functions

偏好純函數

Pure functions are easier to reason about, sometimes easier to optimize (and even parallelize), and sometimes can be memoized.

純函數更容易推理,有時更容易優化(甚至並行化),有時可以記住。

template<class T>
auto square(T t) { return t * t; }

1.9 Unused parameters should be unnamed

未使用的參數應未命名

Readability. Suppression of unused parameter warnings.

可讀性。禁止使用未使用的參數警告。

X* find(map<Blob>& m, const string& s, Hint); 

2 Parameter passing expression rules:

參數傳遞表達式規則:

2.1 Prefer simple and conventional ways of passing information

喜歡簡單和傳統的信息傳遞方式

2.2 For "in" parameters, pass cheaply-copied types by value and others by reference to const

對於「in」參數,按值傳遞廉價複製的類型,並通過引用const傳遞其他類型

2.3 For "in-out" parameters, pass by reference to non-const

對於「in-out」參數,通過引用傳遞非常量

2.4 For "will-move-from" parameters, pass by X&& and std::move the parameter

對於「will-move-from」參數,通過X&& and std::move 傳遞參數

2.5 For "forward" parameters, pass by TP&& and only std::forward the parameter

對於「forward」參數,通過TP&& 並且僅std::forward傳參

2.6 For "out" output values, prefer return values to output parameters

對於「out」輸出值,首選返回值而不是輸出參數

2.7 To return multiple "out" values, prefer returning a struct or tuple

若要返回多個「out」值,請返回結構或元組

2.8 Prefer T* over T& when "no argument" is a valid option

當「無參數」是一個有效選項時,偏向使用T*

A pointer (T*) can be a nullptr and a reference (T&) cannot, there is no valid "null reference". Sometimes having nullptr as an alternative to indicated "no object" is useful, but if it is not, a reference is notationally simpler and might yield better code.

指針(T*)可以是空指針,引用(T&)不能是空,因為沒有有效的「空引用」。有時,使用nullptr作為指示的「no object」的替代是有用的,但如果不是,則引用要簡單得多,可能會產生更好的代碼。

string zstring_to_string(zstring p) // zstring is a char*; that is a C-style string
{
    if (!p) return string{};    // p might be nullptr; remember to check
    return string{p};
}

void print(const vector<int>& r)
{
    // r refers to a vector<int>; no check needed
}

3 Parameter passing semantic rules:

傳遞語義規則的參數:

3.1 Use T* or owner<T*> to designate a single object

使用T*或owner<T*>指定單個對象

3.2 Use a not_null<T> to indicate that "null" is not a valid value

使用not_null<T>表示「null」不是有效值

3.3 Use a span<T> or a span_p<T> to designate a half-open sequence

使用span<T>或span<p<T>指定半開放序列

3.4 Use a zstring or a not_null<zstring> to designate a C-style string

使用zstring或notúnull<zstring>指定C樣式字符串

3.5 Use a unique_ptr<T> to transfer ownership where a pointer is needed

在需要指針的地方使用unique_ptr<T>來轉換所有資源所有權

3.6 Use a shared_ptr<T> to share ownership

使用一個shared_ptr<T>來共享資源所有權

4 Value return semantic rules:

值返回語義規則:

4.1 Return a T* to indicate a position (only)

返回T*表示位置(僅)

That's what pointers are good for. Returning a T* to transfer ownership is a misuse.

這就是指針的好處。返回T*以轉讓所有權是一種濫用。

Node* find(Node* t, const string& s)  // find s in a binary tree of Nodes
{
    if (!t || t->name == s) return t;
    if ((auto p = find(t->left, s))) return p;
    if ((auto p = find(t->right, s))) return p;
    return nullptr;
}

If it isn't the nullptr, the pointer returned by find indicates a Node holding s. Importantly, that does not imply a transfer of ownership of the pointed-to object to the caller.

如果不是nullptr,find返回的指針表示節點持有s。重要的是,這並不意味著將指向對象的所有權轉移給調用方。

4.2 Never (directly or indirectly) return a pointer or a reference to a local object

從不(直接或間接)返回指向本地對象的指針或引用

4.3 Return a T& when copy is undesirable and "returning no object" isn't needed

當不需要複製並且不需要「返回空對象」時返回T&

4.4 Don't return a T&&

不要返回T&&

4.5 int is the return type for main()

int是main()的返回類型

4.6 Return T& from assignment operators

從賦值運算符重載返回T&

4.7 Don't return std::move(local)

不返回std::move(局部)

5 Other function rules:

其他功能規則:

5.1 Use a lambda when a function won't do (to capture local variables, or to write a local function)

當函數不起作用時使用lambda(捕捉局部變量或編寫局部函數)

Functions can't capture local variables or be defined at local scope; if you need those things, prefer a lambda where possible, and a handwritten function object where not. On the other hand, lambdas and function objects don't overload; if you need to overload, prefer a function (the workarounds to make lambdas overload are ornate). If either will work, prefer writing a function; use the simplest tool necessary.

函數不能捕獲局部變量或在局部範圍內定義;如果需要這些東西,請儘可能選擇lambda,否則選擇手寫函數對象。另一方面,lambdas和函數對象不會重載;如果需要重載,請選擇一個函數(使lambdas重載的解決方案是華麗的)。如果這兩種方法都有效,最好編寫一個函數;使用必要的最簡單工具。

// writing a function that should only take an int or a string
// -- overloading is natural
void f(int);
void f(const string&);

// writing a function object that needs to capture local state and appear
// at statement or expression scope -- a lambda is natural
vector<work> v = lots_of_work();
for (int tasknum = 0; tasknum < max; ++tasknum) {
    pool.run([=, &v]{
        /*
        ...
        ... process 1 / max - th of v, the tasknum - th chunk
        ...
        */
    });
}
pool.join();

5.2 Where there is a choice, prefer default arguments over overloading

在有選擇的地方,首選默認參數而不是重載

5.3 Prefer capturing by reference in lambdas that will be used locally, including passed to algorithms

偏向在本地使用的lambdas中通過引用捕獲,包括傳遞給算法

5.4 Avoid capturing by reference in lambdas that will be used nonlocally, including returned, stored on the heap, or passed to another thread

避免在lambdas中通過引用捕獲將在非本地使用的內容,包括返回、存儲在堆中或傳遞給另一個線程

5.5 If you capture this, capture all variables explicitly (no default capture)

如果捕獲此變量,則顯式捕獲所有變量(無默認捕獲)

5.6 Don't use va_arg arguments

不要使用va_arg參數

ref:

https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#S-functions

-End-

關鍵字: