19

パラダイム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この質問で説明した理由により、参照ではなく値で結果を返すのバージョンを定義するのが良いプログラミング方法である現実の状況があるでしょうか?

4

2 に答える 2

17

はい、ぶら下がり参照です。

はい、それは落とし穴です。

値によって返されるバージョンは、デフォルトで使用し、必要に応じて参照渡しに切り替える場合に便利です。デフォルトで参照によるものを使用し、必要なときにのみ値によるものに切り替える場合でも、問題が発生することになりますconst A a_= ::max(A(), A());

残念ながら、値によるものには新しい落とし穴があります。

A a, b, c;
mymax(a, b) = c; // assigns to temporary, not to a or b

(実際、このコードを見ると、それを呼び出すとmax_by_val、これを書かないと思います)。

const 値で返すことができ、一部の人々は、演算子のオーバーロードが const 値で返されることを推奨していました。しかし、これにより C++11 でパフォーマンスの問題が発生します。

A a, b, c;
c = constmymax(a, b); // copies from const rvalue instead of moving.

C++03 でも、同等の明示的な最適化が妨げられswap(c, constmymax(a,b))ます。

于 2012-12-05T11:12:19.270 に答える
5

(回答は削除されました)一時変数への const 参照のバインドに関する Herb Suttersの GOTW 記事を参照してください。

編集:実際には間違っていたので回答を削除しました(明確にしてくれてありがとう)。残っているのは、多少関連する記事へのリンクだけです。ただし、注意してください:この記事で説明されている機能は、ここでは適用されません!

于 2012-12-05T11:13:02.733 に答える