2015-12-30 11 views
14

少し概要。私は強力なtypedefを提供するクラステンプレートを書いています。強力なtypedefによって、私はエイリアスを宣言した通常のtypedefとは対照的です。std :: vectorとstd :: stringの比較演算子がテンプレート関数として定義されているのはなぜですか?

using EmployeeId = StrongTypedef<int>; 

は今、強い型定義と暗黙の変換上の思考の別の学校があります。アイデアを与えるために。これらの学校の1つは、すべての整数がEmployeeIdであるわけではありませんが、すべてのEmployeeIdは整数なので、EmployeeIdから暗黙の変換を整数に許可する必要があります。そして、あなたはこれを実装し、のようなものを書くことができます:xは暗黙的に整数に変換される、その後、整数等価比較が使用されているので、

EmployeeId x(4); 
assert(x == 4); 

これは動作します。ここまでは順調ですね。

std::vector<int> v1{1,2}; 
EmployeeScores e(v1); 
std::vector<int> v2(e); // implicit conversion 
assert(v1 == v2); 

しかし、私はまだこれを行うことはできません:だから私はこのようなことを行うことができます

using EmployeeScores = StrongTypedef<std::vector<int>>; 

:今、私は整数のベクトルでこれをやりたい

assert(v1 == e); 

これがうまくいかない理由は、std::vectorがその等価性チェックをどのように定義しているかにあります。

template <class T, class A> 
bool operator==(const vector<T,A> & v1, const vector<T,A> & v2) { 
... 
} 

これは関数テンプレートです。ルックアップの初期段階で破棄されるため、暗黙的にベクトルに変換される型を比較す​​ることはできません。平等を定義するための

別の方法は次のように次のようになります。この第2のケースで

template <class T, class A = std::allocator<T>> 
class vector { 
... // body 

    friend bool operator==(const vector & v1, const vector & v2) { 
    ... 
} 

} // end of class vector 

、等価演算子は次のように、それはクラスと一緒に生成されています普通の機能ですが、関数テンプレートではありませんメンバー関数これは友人のキーワードによって可能になる珍しいケースです。

質問がありました(ごめんなさい、背景が長かったです)、なぜstd::vectorは最初のフォームの代わりに2番目のフォームを使用していませんか?これにより、vectorはプリミティブ型のように振る舞います。そして、あなたがはっきりとわかるように、私のユースケースに役立ちます。 stringでは、stringがクラステンプレートの単なるtypedefであることを忘れやすいので、この動作はさらに驚くべきことです。

友人の関数がクラスで生成されるため、ベクトルの含まれた型が等価比較をサポートしていないと、ハードエラーが発生することがあります。これはそうではありません;テンプレートクラスのメンバ関数のように、未使用の場合は生成されません。第2に、より一般的なケースでは、フリー関数は、クラスと同じヘッダに定義する必要がないという利点があり、利点があります。しかし、これは明らかにここで利用されていません。

だから、何ができますか?これには正当な理由があるのですか、それとも最適ではない選択ですか?

編集:私は2つのことを示す簡単な例を書いた:暗黙の変換は両方とも、友人のアプローチでは望みどおりに動作し、テンプレート化された型が等価演算子の要件を満たさないその場合、等価演算子が使用されていないと仮定すれば明らかです)。編集:最初のアプローチ:http://coliru.stacked-crooked.com/a/6f8910945f4ed346と対照的に改善されました。

+1

なぜこれがダウンリストされましたか? – Untitled123

+1

'friend'versionを使って、' assert(v1 == e); 'がコンパイルされますか? – YSC

+0

@YSCはい、私はColiruへのリンクを追加しました。あなたの質問に完全に対処すれば教えてください。 –

答えて

0

編集:私の説明を再読み込みし、周りのいくつかのコメントの影響を受けた後、私は私の元の推論が確かに魅力的ではないと確信しています。私の答えは、値xが異なるタイプの値yに暗黙的に変換される可能性はあるが、2つの間の「自動」等価比較が必ずしも期待できないと主張している。文脈化のために、私はここで例として使用したコードを残しています。

struct B {}; 

template <class T> 
struct A { 
    A() {} 
    A(B) {} 
    friend bool operator==(const A<T>&, const A<T>&) { return false; } 
}; 

// The template version wouldn't allow this to happen. 
// template <class T> 
// bool operator==(const A<T>&, const A<T>&) { return false; } 

int main() { 
    A<B> x; 
    B y; 
    if (x == y) {} //compiles fine 
    return 0; 
} 
+0

'A'は' B'から ''に暗黙的に変換可能であると主張しています。より良いバージョンは '演算子A'を持つ' B'を持っていますが、どちらの場合でも不変式が違反していると、変換コードが悪いということがほとんどです。私の大きな関心事はコストですが、高価な暗黙の変換もコード臭です。 – Yakk

+0

AにBからの非明示的なコンストラクタを与えることによって、Aを受け入れるすべてがBを受け入れることに同意したことになります。また、規格上、Aはベクトルの役割を担い、ベクトルはそのようなコンストラクタを持たない。 –

+0

可視性の問題は存在しますが、実際にはシンタックス演算子==(...)を使用して実際に呼び出しを行うと、それだけの違いが生じます。したがってこれを変更すると、後方互換性がなくなります。しかし、誰も本当にこれをやりたいと思っているわけではないので、最初はこのような理由が説明されていません。 –

2

あなたが記述techiqueは(私はケーニッヒ演算子を呼んで)知られていなかった、少なくともない広く、ポイントvectorで設計され、最初に指定します。

これを変更すると、もともとの使用よりも注意が必要になり、さらに正当化が必要になります。

今日、ケーニッヒ演算子はテンプレート演算子の代わりに使用されると思われます。

+0

"今日のKoenig演算子は、テンプレート演算子の代わりに使用されるでしょう。疑わしい。 'basic_string_view'は、暗黙的な変換を処理する代わりに、「十分な追加のオーバーロード」ルールを追加しました。 –