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::move
讓o
無法使用,是因為建立save_o
的時候會呼叫HeavyObject(const HeavyObject&&)
導致的。