パラダイムmax
テンプレート関数を考えてみましょうstd::max()
。
// From the STL
// TEMPLATE FUNCTION _Debug_lt
template<class _Ty1, class _Ty2> inline
bool _Debug_lt(const _Ty1& _Left, const _Ty2& _Right,
_Dbfile_t _File, _Dbline_t _Line)
{ // test if _Left < _Right and operator< is strict weak ordering
if (!(_Left < _Right))
return (false);
else if (_Right < _Left)
_DEBUG_ERROR2("invalid operator<", _File, _Line);
return (true);
}
// intermediate #defines/templates skipped
// TEMPLATE FUNCTION max
template<class _Ty> inline
const _Ty& (max)(const _Ty& _Left, const _Ty& _Right)
{ // return larger of _Left and _Right
return (_DEBUG_LT(_Left, _Right) ? _Right : _Left);
}
... または、より単純な形式で:
template<typename T> inline
T const & max(T const & lhs, T const & rhs)
{
return lhs < rhs ? rhs : lhs;
}
テンプレートが参照によって返される必要がある理由を理解しmax
ています (高価なコピーを回避し、コピー コンストラクターの要件を回避するため)。
しかし、これは次のコードのように参照がぶら下がっている可能性につながりませんか?
int main()
{
const int & max_ = ::max(3, 4);
int m = max_; // Is max_ a dangling reference?
}
この場合、コードは正常にビルドおよび実行され (VS 2010)、値m
は 4 に設定されます。ただし、max_
ハードコーディングされた右辺値3
および4
が に直接渡されmax
、これらに割り当てられたストレージがあるため、これはダングリング リファレンスであることがわかります。ハードコーディングされた右辺値定数は、次のコード行に到達するまでに正当に割り当て解除できます。
次のような本格的なオブジェクトの類似のエッジケースを想像できます。
class A
{
friend bool operator<(A const &, A const &)
{
return false;
}
};
int main()
{
const A & a_ = ::max(A(), A());
A a = a_; // Is a_ a dangling reference?
}
この使用法max
- 呼び出し引数リスト内で定義された右辺値が引数として渡される - は、回避するために参照を返すように定義する必要があるためmax
、避けられないの使用による潜在的な「落とし穴」の例であると訂正しますか?max
高価なコピー(およびコピーコンストラクターの要件を回避するため)?
max
この質問で説明した理由により、参照ではなく値で結果を返すのバージョンを定義するのが良いプログラミング方法である現実の状況があるでしょうか?