boost::reference_wrapper<T>
明示的な T&
コンストラクターstd::reference_wrapper<T>
があり、暗黙的なコンストラクターがあります。したがって、次のコードでは:
foo = bar;
foo
がの場合boost::reference_wrapper
、コードはコンパイルに失敗します(実際の参照と同じセマンティクスをreference_wrapper
持たないため、これは適切です。
foo
がの場合、コードはへの参照をstd::reference_wrapper
「再バインド」します(誤って期待する可能性があるため、値を割り当てる代わりに)。foo
bar
これにより、とらえどころのないバグが発生する可能性があります...次の例を検討してください。
いくつかの架空のライブラリのバージョン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&
に「再バインド」されるため、それを渡すコードをサイレントに中断します。a
b
私の質問:なぜ標準委員会は、コンストラクターをフォローして明示的にするのではなく、暗黙の変換(fromT&
から)を許可することを選択したのですか?std::reference_wrapper<T>
boost
T&
編集:( ジョナサン・ウェイクリーによって提供された回答に応じて)...
元のデモ(上記のセクション)は、ライブラリを微妙に変更するstd::reference_wrapper
と、アプリケーションにバグが発生する可能性があることを示すために意図的に簡潔になっています。
次のデモは、reference_wrapper
Jonathan 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
、利便性のために安全性を損なうように思われます。これは、言語/ライブラリーの設計ではめったに発生しないはずです。