Function

這例會介紹如何設計一個 function 的介面。


如果不會丟出excception,宣告成noexcept

如果一個 function 宣告成noexcept,compile time 會針對noexcept作不同的事情,所以如果確定一個 function 不會丟出exception,或是它是一個 C style 的 function 請宣告成noexcept

void not_throw() noexcept
{
    ...
}

參考stl_vector.h

// Do move assignment when it might not be possible to move source
// object's memory, resulting in a linear-time operation.
void _M_move_assign(vector&& __x, std::false_type)
{
    if (__x._M_get_Tp_allocator() == this->_M_get_Tp_allocator())
        _M_move_assign(std::move(__x), std::true_type());
    else {
        // The rvalue's allocator cannot be moved and is not equal,
        // so we need to individually move each element.
        this->assign(std::__make_move_if_noexcept_iterator(__x.begin()),
                     std::__make_move_if_noexcept_iterator(__x.end()));
        __x.clear();
    }
 }

Input 參數應該用T還是用T&

如果一個物件小於 4 ~ 6 個 byte 請使用T,否則使用T&

Example
void fn(int i);           // OK

void fn(const int& i);    // BAD, overhead

void fn(string s);        // BAD, expensive

void fn(const string& s); // OK

使用 return value 比使用 output parameter 更好

使用 return value

  • 可讀性高
  • C++11 後編譯器會作最佳化,不會有因為 copy 或 move 造成的效率問題。
Example, bad
void fn(vector<int>& v1, vector<int>& v2);// BAD, 哪一個是 output ?

void fn(int&); // BAD, overhead
Copy elision
class T {};

T x = T(T(T())); // 只會呼叫一次 constructor

每個編譯器最佳化的方式可能會不一樣,例如下面的程式,用不同的編譯器會造成不同的結果

struct C {
  C() {}
  C(const C&) { std::cout << "A copy was made.\n"; }
};

C f() {
  return C();
}

int main() {
  std::cout << "Hello World!\n";
  C obj = f();
}

可能的結果

Hello World! 
A copy was made. 
A copy was made.
Hello World! 
A copy was made.
Hello World!

回傳多個 output

Example, C++11 可以使用tuple
std::tuple<double, char, std::string> callee()
{
    std::make_tuple(3.8, 'A', "Lisa Simpson");
}

void caller()
{
    auto o = callee();

    cout << std::get<0>(o) << endl;
    cout << std::get<1>(o) << endl;
    cout << std::get<2>(o) << endl;
}
Example, C++17 可以使用 Structured binding declarations
struct A {
    int i;
    bool b;
};

struct A callee()
{
    struct A a = { 123, true };
    return a;
}

void caller()
{
    auto [ val1, val2 ] = callee();

    cout << val1 << endl;  // A.i
    cout << val2 << endl;  // A.b
}

使用T&&來增進效率

如果某個參數給 function 使用後就不再被其他人使用,可以使用T&&std::move來增進效率。

Example, bad
class HeavyObject{ ... };

void consume(const HeavyObject& o)
{
    HeavyObject save_o(o);         // Bad, 呼叫 HeavyObject(const HeavyObject&) copy 成本很高
}

void fn()
{
    HeavyObject o;

    consume(o);

    // After consume(), o is never used.
}
Example, good
class HeavyObject{ ... };

void consume(HeavyObject&& o)
{
    HeavyObject save_o(std::move(o));         // Good, 呼叫 HeavyObject(const HeavyObject&&) 比 copy 快
}

void fn()
{
    HeavyObject o;

    consume(std::move(o));

    // o 不可以再被使用
}

注意:不是因為std::moveo無法使用,是因為建立save_o的時候會呼叫HeavyObject(const HeavyObject&&)導致的。


Reference

results matching ""

    No results matching ""