2

今週の達人 #2 より。元の関数があります:

string FindAddr( list<Employee> l, string name )
{
    for( list<Employee>::iterator i = l.begin(); // (1)
         i != l.end(); 
         i++ ) 
    {
        if( *i == name ) // (2)
        {
            return (*i).addr;
        }
    }
    return "";
}

それにダミーの Employee クラスを追加しました:

class Employee
{
    string n;
public:
    string addr;

    Employee(string name) : n(name) {}
    Employee() {}

    string name() const
    {
        return n;
    }

    operator string()
    {
        return n;
    }
};

コンパイルエラーが発生しました:

その場で (1):

conversion from ‘std::_List_const_iterator<Employee>’ to non-scalar type ‘std::_List_iterator<Employee>’ requested

その場で (2):

no match for ‘operator==’ in ‘i.std::_List_iterator<_Tp>::operator* [with _Tp = Employee]() == name’

最初のものを削除するには、 に変更iteratorconst_iteratorます。2 番目のエラーを解消する唯一の方法は、独自の operator== を記述することです。しかし、ハーブ・サッターは次のように書いています。

Employee クラスは示されていませんが、これが機能するには、文字列への変換または文字列を受け取る変換 ctor が必要です。

しかし、Employee には変換関数と変換コンストラクタがあります。GCC バージョン 4.4.3。g++ file.cppフラグなしで正常にコンパイルされました。

暗黙的な変換が必要であり、機能するはずですが、なぜ機能しないのですか? operator== は必要ありません。Sutter が言ったように、 string への変換または string を取る変換 ctor を使用して動作させたいだけです。

4

5 に答える 5

3

この場合、Herb Sutter は間違っています (私は「Exceptional C++」のコピーを持っていませんが、この GotW エントリが本のためにクリーンアップされることを期待しています.)

ただし、最初に、問題のエラーに到達するには、パラメーター宣言constからを削除する必要があります。l(注意: に置き換えるiteratorconst_iterator、問題が難読化されるだけです: your operator string()is not const、つまり、定数 object に対して呼び出し可能ではないことを意味します*i)。

最初の問題を修正すると、コードは実際にコンパイルに失敗します

if( *i == name )

ライン。これは、オブジェクトstd::operator ==を比較する関数std::stringが、実際には標準ライブラリによってテンプレート関数として定義されているために発生します。

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

この関数がオーバーロードの解決に参加するには、そのテンプレート引数が正常に推定される必要があります。ある引数が であり、別の*i == name引数がであるため、これはあなたのコンテキストでは不可能です。テンプレート引数の推測は失敗し、このため、このテンプレート関数はオーバーロードの解決とは見なされません。他に候補がないため、コンパイラはエラーを報告します。std::stringEmployee

このため、クラスにoperator string()変換関数が存在する場合にコードがコンパイル可能であるべきであるという Herb Sutter の主張は正しくありません。Employeeコードは、専用の非テンプレート比較演算子を宣言する標準ライブラリの特定の実装でコンパイルされる場合がありstd::stringますが、通常、標準ライブラリの実装はそのようにはしません。

彼はまた別の根拠のない主張をしており、その回心の結果は一時的なものでなければならないと主張しています。実際には、Employeeクラスにはoperator const string &() const一時的なものを作成しない変換関数を含めることができます(例で実行できるように、代わりにデータメンバーへの参照を返します)。

最後に、変換コンストラクターがこのコードを機能させるという彼の主張は、プログラムが比較専用operator ==を宣言している場合にのみ当てはまります。Employee vs. Employeeこのような専用の演算子を導入しないと、変換コンストラクターはこのコードの有効性に影響を与えません。つまり、あなたの例では、コンストラクターを宣言するEmployee(string name)意味がありませんでした-何も達成しません。

于 2012-11-07T22:53:56.840 に答える
3

コードのコンパイルは許可されていますが、必須ではないと思います。stringは の typedef でbasic_string<char>あり、それ自体はクラスではないことに注意してください。最小限の例:

template <typename T>
struct basic_string
{
};

typedef basic_string<char> string;

struct Employee
{
    operator string() const;
};

template <typename T>
bool operator==(const basic_string<T>& a, const basic_string<T>& b);

// Compilation succeeds when this is uncommented
// bool operator==(const basic_string<char>& a, const basic_string<char>& b);

bool f(const Employee& e, const string& s)
{
    return e == s; // error
}

はい、Employeeに変換stringできますが、実際にはstringではないため、どのテンプレート引数を に使用するかを決定するには十分ではありませんoperator==

専用の追加のオーバーロードoperator==(const string&, const string&)が追加された場合、それは機能し、別の標準ライブラリ実装がそのオーバーロードを提供する可能性があります。提供されている場合、コードはコンパイルされますが、標準の C++ では必要のない拡張機能です。

編集:実際には、他の人が述べたように、それでは十分ではありません(const問題)が、他の問題を修正してもこれは残ります。これがあなたの中心的な質問に対する答えだと思います.

于 2012-11-07T23:02:42.557 に答える
1

免責事項:質問のコードは、私がこれを書いて投稿するまでにすでに変更されていました。コードはもはや GOTW コードの引用ではありません。それは別のコードであり、疑わしいコンパイルエラーも明らかに正しくありませんが、主に他の問題に関係しているため、(元の投稿に対する) この回答をそのままにしておきます (対応する編集によって質問に対する一連の編集を追跡するつもりはありません)。この回答を作り直します)。

@Vaibhavは、変換を明示的に表現する必要があるというコアな質問にすでに答えています。

しかし、引用された GOTW (Guru Of The Week) は不必要な一時的なものに関係しているので、あなたのクラスEmployeeコードは、

class Employee
{
    string n;
public:
    string addr;

    Employee(string name) : n(name) {}
    Employee() {}

    string name() const
    {
        return n;
    }

    operator string()
    {
        return n;
    }

};

その GOTW で説明されているいくつかの落とし穴を繰り返します。

Employee(string name) : n(name) {}

文字列引数を値で取得することは、C++11 では問題ありません。とにかくコピーが作成されるからです。しかし、moveこの値をメンバーに入れる必要があります。

Employee(string name) : n(move(name)) {}

次に、あなたの

operator string()
{
    return n;
}

ではないconstため、オブジェクトで呼び出すことができず、constこの演算子を呼び出すためにそのオブジェクトを不必要にコピーする必要があります。

したがって、技術的には、

operator string() const
{
    return n;
}

しかし、設計レベルではこれも間違っています。従業員は文字列ではありません。従業員はどの文字列に変換すると予想されますか? 彼または彼女の名前は?社員コード?社会保障番号?

暗黙の変換は一般的に厄介であり、これはその例です。それを作るのは役に立ちませんexplicit。関連する文字列は名前付き操作を介して既に利用可能であるため (これは良いことです)、この変換演算子を削除するだけでクラスが大幅に改善されます。

于 2012-11-07T23:00:13.367 に答える
0

イテレータから Employee オブジェクトを受け取るので、 *i は Employee オブジェクトを指しています。次に、その従業員の名前を指す必要があります。

これを試して:

if( (*i).name == name )
于 2012-11-07T22:45:34.453 に答える
-1

比較演算子のオペランドを逆にするとどうなりますか? コンパイラは、 for がないと文句を言いますが、operator ==forList<>が定義されていstringます。

に変更if( *i == name )するif( name == *i )とうまくいくはずです。

于 2012-11-07T22:54:05.040 に答える