24

boost::reference_wrapper<T>明示的な T&コンストラクターstd::reference_wrapper<T>があり、暗黙的なコンストラクターがあります。したがって、次のコードでは:

foo = bar;

fooがの場合boost::reference_wrapper、コードはコンパイルに失敗します(実際の参照と同じセマンティクスをreference_wrapper持たないため、これは適切です。

fooがの場合、コードはへの参照をstd::reference_wrapper「再バインド」します(誤って期待する可能性があるため、値を割り当てる代わりに)。foobar

これにより、とらえどころのないバグが発生する可能性があります...次の例を検討してください。

いくつかの架空のライブラリのバージョン1.0では:

void set_max(int& i, int a, int b) {
    i = (a > b) ? a : b;
}

新しいバージョン(1.1)ではset_max、インターフェイスを変更せずに任意の幅(またはUDT)の整数を受け入れるようにテンプレートに変換されます。

template<typename T1, typename T2, typename T3>
void set_max(T1& i, T2 a, T3 b) {
    i = (a > b) ? a : b;
}

そして最後に、ライブラリを使用するアプリケーションでは、次のようになります。

// i is a std::reference_wrapper<int> passed to this template function or class
set_max(i, 7, 11);

この例では、ライブラリset_maxは呼び出しインターフェイスを変更せずにの実装を変更します。std::reference_wrapperこれは、引数が変換されなくなり、代わりにぶら下がっている参照(または)int&に「再バインド」されるため、それを渡すコードをサイレントに中断します。ab

私の質問:なぜ標準委員会は、コンストラクターをフォローして明示的にするのではなく、暗黙の変換(fromT&から)を許可することを選択したのですか?std::reference_wrapper<T>boostT&


編集:( ジョナサン・ウェイクリーによって提供された回答に応じて)...

元のデモ(上記のセクション)は、ライブラリを微妙に変更するstd::reference_wrapperと、アプリケーションにバグが発生する可能性があることを示すために意図的に簡潔になっています。

次のデモは、reference_wrapperJonathan Wakelyの指摘に応えて、「インターフェイスを介して参照を渡す」ための実際の合法的な使用法を示すために提供されています。

  • 開発者/ベンダーAから

似ているstd::bindが、あるタスクに特化したふりをするもの:

template<typename FuncType, typename ArgType>
struct MyDeferredFunctionCall
{
    MyDeferredFunctionCall(FuncType _f, ArgType _a) : f(_f), a(_a) {}

    template<typename T>
    void operator()(T t) { f(a, t); }

    FuncType f;
    ArgType a;
};
  • 開発者/ベンダーBから

RunningMaxファンクタークラス。この架空のライブラリのバージョン1.0と1.1の間で、の実装はRunningMax、呼び出しインターフェイスを変更せずに、より一般的なものに変更されました。このデモでは、古い実装は名前空間lib_v1で定義されていますが、新しい実装は次のように定義されていlib_v2ます。

namespace lib_v1 {
    struct RunningMax {
        void operator()(int& curMax, int newVal) {
                if ( newVal > curMax ) { curMax = newVal; }
            }
    };
}
namespace lib_v2 {
    struct RunningMax {
        template<typename T1, typename T2>
        void operator()(T1& curMax, T2 newVal) {
                if ( newVal > curMax ) { curMax = newVal; }
            }
    };
}
  • 最後になりましたが、上記のすべてのコードのエンドユーザーは次のとおりです。

ベンダー/開発者AおよびBのコードを使用してタスクを実行する開発者:

int main() {
    int _i = 7;
    auto i = std::ref(_i);
    auto f = lib_v2::RunningMax{};

    using MyDFC = MyDeferredFunctionCall<decltype(f), decltype(i)>;
    MyDFC dfc = MyDFC(f, i);
    dfc(11);

    std::cout << "i=[" << _i << "]" << std::endl; // should be 11
}


次の点に注意してください。

  • エンドユーザーはstd::reference_wrapper、意図した方法を使用します。

  • 個々に、どのコードにもバグや論理的な欠陥はなく、すべてがベンダーBのライブラリの元のバージョンで完全に機能しました。

  • boost :: reference_wrapperはライブラリのアップグレード時にコンパイルに失敗しますが、std :: reference_wrapperは、回帰テストでキャッチされる場合とされない場合があるバグをサイレントに導入します。

  • 「再バインド」は、などのツールがキャッチするメモリエラーではないため、このようなバグを追跡することは困難です。valgrindさらに、誤用の実際のサイトは、エンドユーザーではなく、std::reference_wrapperベンダーBのライブラリコード内にあります。

結論: boost::reference_wrapperコンストラクターを明示的に宣言することでより安全に見えT&、このようなバグの導入を防ぐことができます。で明示的なコンストラクター制限を削除するという決定はstd::reference_wrapper、利便性のために安全性を損なうように思われます。これは、言語/ライブラリーの設計ではめったに発生しないはずです。

4

2 に答える 2

3

std::reference_wrapperこれは、引数が変換されなくなり、int&代わりにぶら下がっている参照(aまたはb)に「再バインド」されるため、aを渡すコードをサイレントに中断します。

だからそうしないでください。

reference_wrapper値によるコピーを作成するインターフェイスを介して参照を渡すためのものであり、任意のコードに渡すためのものではありません。

また:

// i is a std::reference_wrapper<int> (perhaps b/c std::decay wasn't used)

decay何も変更されません。参照ラッパーには影響しません。

于 2013-03-27T09:48:08.977 に答える
2

暗黙の変換T&-> reference_wrapper<T>)が許可されているがstd::reference_wrapper<T>、許可されていない 理由は、NateKohlが提供するDR-689リンクboost::reference_wrapper<T>で十分に説明されています。要約する:

2007年、C ++ 0x / C ++ 11ライブラリワーキンググループ(LWG)は、標準のセクションへの変更# DR-689を提案しました。20.8.3.1 [refwrap.const]

reference_wrapperのコンストラクターは現在明示的です。この背後にある主な動機は、[DR-688]の提案された解決策によって対処される、右辺値に関する安全性の問題です。したがって、暗黙的な変換の要求はリサーフェシングを続けるため、コンストラクターの要件を緩和することを検討する必要があります。

提案された解決策: reference_wrapperのコンストラクターからexplicitを削除します。

指摘する価値があります:

  • boost::reference_wrapperはそのように緩和されておらず、boost::reference_wrapperとのセマンティクスの間に矛盾を生じさせる提案もありませんstd::reference_wrapper

  • DR-689の言い回し(具体的には「要求が浮上し続ける」部分)に基づくと、この変更は、LWGによって、安全性と利便性の間の許容可能なトレードオフとして単純に見なされたようです(ブーストの対応物とは対照的)。

  • DR-689で言及されている唯一のリスクは、右辺値へのバインドのリスクであったため(前のエントリ、DRで説明および解決されたように)、LWGが他の潜在的なリスク(このページの例で示されているものなど)を予測したかどうかは不明です。 -688)。

于 2013-04-01T22:24:52.003 に答える