17

タイプ のオブジェクトがありT、それを参照ラッパーに入れたいとします。

int a = 5, b = 7;

std::reference_wrapper<int> p(a), q(b);   // or "auto p = std::ref(a)"

if (p < q)参照ラッパーにはラップされた型への変換があるため、これで簡単に言うことができます。すべてが順調で、参照ラッパーのコレクションを元のオブジェクトと同じように処理できます。

(以下にリンクされている質問が示すように、これは既存のコレクションの代替ビューを生成する便利な方法であり、元のコレクションとの更新の整合性を維持するだけでなく、完全なコピーのコストを負担することなく自由に再配置できます。 )


ただし、一部のクラスではこれが機能しません。

std::string s1 = "hello", s2 = "world";

std::reference_wrapper<std::string> t1(s1), t2(s2);

return t1 < t2;  // ERROR

私の回避策は、この回答のように述語を定義することです *; しかし、私の質問は次のとおりです。

演算子を参照ラッパーに適用し、ラップされた型の演算子を透過的に使用できるのはなぜですか? なぜ失敗するのstd::stringですか?std::stringテンプレートインスタンスであるという事実と何の関係があるのでしょうか?

*)更新:回答に照らして、使用std::less<T>()は一般的な解決策のようです。

4

2 に答える 2

9

編集:私の推測を一番下に移動しました。これが機能しない理由の規範的なテキストがここにあります。TL;DR バージョン:

関数パラメーターに推定テンプレート パラメーターが含まれている場合、変換は許可されません。


§14.8.3 [temp.over] p1

[...] その名前への呼び出しが (明示的に、または演算子表記法を使用して暗黙的に) 記述されると、テンプレート引数推定 (14.8.2) および明示的なテンプレート引数のチェック (14.3) が各関数テンプレートに対して実行され、検索されます。呼び出し引数で呼び出すことができる関数テンプレートの特殊化をインスタンス化するために、その関数テンプレートで使用できるテンプレート引数値 (存在する場合)。

§14.8.2.1 [temp.deduct.call] p4

[...] [注: 14.8.1 で指定されているように、テンプレート引数推定に参加するテンプレートパラメーターがパラメーターに含まれていない場合、関数引数に対して暗黙的な変換が実行され、対応する関数パラメーターの型に変換されます。[...] —エンドノート]

§14.8.1 [temp.arg.explicit] p6

暗黙的な変換 (第 4 節) は、関数の引数に対して実行され、対応する関数パラメーターの型に変換されます (パラメーターの型に、テンプレート引数推定に関与するテンプレートパラメーターが含まれていない場合)。[注:テンプレート パラメーターは、明示的に指定されている場合、テンプレート引数推定に参加しません。[...] —エンドノート]

推定テンプレート パラメータ ( 、 ) にstd::basic_string依存するため、変換は許可されません。CharTTraits


これは一種の鶏と卵の問題です。テンプレート引数を推測するには、 の実際のインスタンスが必要ですstd::basic_string。ラップされた型に変換するには、変換ターゲットが必要です。そのターゲットは、クラス テンプレートではなく、実際の型である必要があります。コンパイラは、変換演算子またはそのようなものに対して可能なすべてのインスタンス化をテストする必要がありますがstd::basic_string、これは不可能です。

次の最小限のテストケースを想定します。

#include <functional>

template<class T>
struct foo{
    int value;
};

template<class T>
bool operator<(foo<T> const& lhs, foo<T> const& rhs){
    return lhs.value < rhs.value;
}

// comment this out to get a deduction failure
bool operator<(foo<int> const& lhs, foo<int> const& rhs){
    return lhs.value < rhs.value;
}

int main(){
    foo<int> f1 = { 1 }, f2 = { 2 };
    auto ref1 = std::ref(f1), ref2 = std::ref(f2);
    ref1 < ref2;
}

のインスタンス化にオーバーロードを提供しない場合int、推論は失敗します。そのオーバーロードを提供すると、それは、許可された 1 つのユーザー定義foo<int> const&の変換 (変換ターゲット) に対してコンパイラがテストできるものになります。この場合、変換が一致するため、オーバーロードの解決が成功し、関数呼び出しが取得されます。

于 2011-12-14T23:24:42.693 に答える
6

std::reference_wrapperがないoperator<ため、唯一の方法はメンバーref_wrapper<ref_wrapperを介することです。ref_wrapper

operator T& () const noexcept;

ご存知のように、次のとおりですstd::string

typedef basic_string<char> string;

関連する宣言string<stringは次のとおりです。

template<class charT, class traits, class Allocator>
bool operator< (const basic_string<charT,traits,Allocator>& lhs, 
                const basic_string<charT,traits,Allocator>& rhs) noexcept;

このstring<string関数宣言テンプレートは、一致するstring=によってインスタンス化され、これは=などbasic_string<charT,traits,Allocator>に解決されます。charTchar

std::reference_wrapper(またはその (ゼロ) 基底クラスのいずれか) が一致しないためbasic_string<charT,traits,Allocator>、関数宣言テンプレートを関数宣言にインスタンス化できず、オーバーロードに参加できません。

ここで重要なのは、非テンプレートプロトタイプがないことです。operator< (string, string)

問題を示す最小限のコード

template <typename T>
class Parametrized {};

template <typename T>
void f (Parametrized<T>);

Parametrized<int> p_i;

class Convertible {
public:
    operator Parametrized<int> ();
};

Convertible c;

int main() {
    f (p_i); // deduce template parameter (T = int)
    f (c);   // error: cannot instantiate template
}

与える

In function 'int main()':
Line 18: error: no matching function for call to 'f(Convertible&)'

標準引用

14.8.2.1 関数呼び出しからのテンプレート引数の推定 [temp.deduct.call]

テンプレート引数推定は、以下で説明するように、各関数テンプレート パラメーターの型 (call it P) と呼び出しの対応する引数の型 (call it ) を比較することによって行われAます。

(...)

一般に、演繹プロセスは、演繹されたものを (上記のように型が変換された後に)A同一にするテンプレート引数値を見つけようとします。ただし、違いが認められる 3 つのケースがあります。AA

  • Pの型が参照型の場合、推論されAた型 (つまり、参照によって参照される型) は、変換された型よりも cv 修飾されている可能性がありますA

これは の場合であることに注意してくださいstd::string()<std::string()

  • 変換されたものは、修飾変換 (4.4) を介して推定に変換Aできる別のポインターまたはメンバー型へのポインターにすることができます。A

以下のコメントを参照してください。

  • PクラスでP、形式がsimple-template-idの場合、変換Aされた は deduced の派生クラスになることができますA

コメント

これは、この段落で次のことを意味します。

14.8.1 明示的なテンプレート引数指定 [temp.arg.explicit] /6

暗黙的な変換 (第 4 節) は、関数の引数に対して実行され、対応する関数パラメーターの型に変換されます (パラメーターの型に、テンプレート引数推定に関与するテンプレート パラメーターが含まれていない場合)。

ifは、前に引用したテキストと直接矛盾するため、 if および only if と見なすべきではありません。

于 2011-12-14T23:27:19.917 に答える