天天看點

C++核心準則邊譯邊學-I.4 接口類型應該精準且嚴格

I.4 Make interfaces precisely and strongly typed(接口類型應該精準且嚴格)

Reason(原因)

Types are the simplest and best documentation, improve legibility due to their well-defined meaning, and are checked at compile time. Also, precisely typed code is often optimized better.

類型是最簡單、最好的文檔。經過良好定義的類型可以提高易讀性,也可以在編譯時被檢查。同時,類型明确定義的代碼通常也會被優化得更好。

Example, don't(反面示例)

Consider:(考慮以下代碼)

void pass(void* data);    // weak and under qualified type void* is suspicious      

Callers are unsure what types are allowed and if the data may be mutated as ​

​const​

​ is not specified. Note all pointer types implicitly convert to void*, so it is easy for callers to provide this value.

調用者無法确定函數可以接受何種類型和資料是否可能被修改(由于沒有用const修飾)。注意所有的指針類型都可以隐式轉換為void*,是以調用者很容易(随便)提供一個值。

The callee must ​

​static_cast​

​ data to an unverified type to use it. That is error-prone and verbose.

被調用者必須(通過默契)将資料靜态轉換為未經驗證的類型以便使用它。這樣的代碼易錯且冗長。

Only use ​

​const void*​

​​ for passing in data in designs that are indescribable in C++. Consider using a ​

​variant​

​ or a pointer to base instead.

隻有在傳遞設計上C++無法描述的資料時才可以使用const void* 。否則考慮使用variant或者指向基礎類型的指針作為代替手段。

Alternative: Often, a template parameter can eliminate the ​

​void*​

​​ turning it into a ​

​T*​

​​ or ​

​T&​

​​. For generic code these ​

​T​

​s can be general or concept constrained template parameters.

可選項:模闆參數經常可以消除void*而使用T*或者T&。對于一般的代碼,這裡的T可以是普遍的或者概念限制的模闆參數。

譯者注:concept是C++20已經決定引入的新概念。

Example, bad(反面示例)

Consider:考慮以下代碼:

draw_rect(100, 200, 100, 500); // what do the numbers specify?draw_rect(p.x, p.y, 10, 20); // what units are 10 and 20 in?      

It is clear that the caller is describing a rectangle, but it is unclear what parts they relate to. Also, an ​

​int​

​​ can carry arbitrary forms of information, including values of many units, so we must guess about the meaning of the four ​

​int​

​​s. Most likely, the first two are an ​

​x​

​​,​

​y​

​ coordinate pair, but what are the last two?

調用者在描述一個矩形這一點是明确的,但卻不知道具體描述的是那些方面(四角坐标還是邊長)。同時,整形資料可以攜帶任意形式的資訊,機關也存在很多可能,是以我們必須猜測四個整形參數的含義。前兩個很有可能是x,y坐标對,但是,後兩個呢?

Comments and parameter names can help, but we could be explicit:

注釋和參數名稱可以提供幫助,但是我們可以(通過參數類型)更加清晰地表達:

void draw_rectangle(Point top_left, Point bottom_right);void draw_rectangle(Point top_left, Size height_width);
draw_rectangle(p, Point{10, 20});  // two cornersdraw_rectangle(p, Size{10, 20});   // one corner and a (height, width) pair      

Obviously, we cannot catch all errors through the static type system (e.g., the fact that a first argument is supposed to be a top-left point is left to convention (naming and comments)).

顯然,我們無法通過靜态類型系統捕捉所有錯誤(例如,認為第一個參數是左上角這個事實就是一種慣例(命名和注釋))

Example, bad(反面示例)

Consider:(考慮以下代碼)

set_settings(true, false, 42); // what do the numbers specify?      

The parameter types and their values do not communicate what settings are being specified or what those values mean.

參數的類型和值沒有說明哪種設定将會被修改,也沒有說明值的含義。

This design is more explicit, safe and legible:

下面的設計更清晰、安全和可讀。

alarm_settings s{};s.enabled = true;s.displayMode = alarm_settings::mode::spinning_light;s.frequency = alarm_settings::every_10_seconds;set_settings(s);      

For the case of a set of boolean values consider using a flags enum; a pattern that expresses a set of boolean values.

對于成組使用布爾值的情況,考慮使用枚舉類型;下面的模式可以表示一套布爾值。

enable_lamp_options(lamp_option::on | lamp_option::animate_state_transitions);      

Example, bad(反面示例)

In the following example, it is not clear from the interface what ​

​time_to_blink​

​ means: Seconds? Milliseconds?

在下面的例子中,接口沒有明确time_to_blink的含義:機關是秒還是毫秒?

void blink_led(int time_to_blink) // bad -- the unit is ambiguous{    // ...    // do something with time_to_blink    // ...}
void use(){    blink_led(2);}      

Example, good(範例)()

​std::chrono::duration​

​ types (C++11) helps making the unit of time duration explicit.

std::chrono::duration類型(C++11)可以讓時間間隔的機關更明确。

void blink_led(milliseconds time_to_blink) // good -- the unit is explicit{    // ...    // do something with time_to_blink    // ...}
void use(){    blink_led(1500ms);}      

The function can also be written in such a way that it will accept any time duration unit.

這個函數可以如下設計以便接受任何機關的時間間隔。

template<class rep, class period>void blink_led(duration<rep, period> time_to_blink) // good -- accepts any unit{    // assuming that millisecond is the smallest relevant unit    auto milliseconds_to_blink = duration_cast<milliseconds>(time_to_blink);    // ...    // do something with milliseconds_to_blink    // ...}
void use(){    blink_led(2s);    blink_led(1500ms);}      

Enforcement(實施建議)

  • (Simple) Report the use of​

    ​void*​

    ​ as a parameter or return type.

     (簡單)報告使用void*作為參數或傳回值的情況。

  • (Simple) Report the use of more than one​

    ​bool​

    ​ parameter.

     (簡單)報告以多個布爾值為參數的情況。

  • (Hard to do well) Look for functions that use too many primitive type arguments.

    (很難做好)找到使用太多原始類型參數的函數。