0

const <Container>&返されたコンテナを外部で変更したくないため、クラスには返されるメソッドがあり、コピーにはコストがかかる可能性があります。たとえば、必要に応じて const 参照を空に返すことができるようにしconst std::set<ClassA>& foo()たいと考えています。すなわちfoo()std::set<ClassA>

const std::set<ClassA>& foo(const std::string& key) {
    std::map<std::string, std::set<ClassA>>::iterator itr = m_elements.find(key);
    return (itr != m_elements.end()) ? *itr : /*empty std::set<ClassA>*/;
}

std::set<ClassA>しかし、一時的に構築された空の inへの const 参照を実際に返すことはできませんfoo()。これを解決するために、一般的な場所でジェネリック テンプレート シングルトン クラスを定義して、任意の型で使用できるようにしています。

template <typename T> class cNull
{
  public:
    static const T& Value() {
      static cNull<T> instance;
      return instance.d;
    }
  private:
    cNull() {};
    cNull(const cNull<T>& src);
    void operator=(cNull<T> const&);
    T d;
};

だから今foo()のようなものになることができます

const std::set<ClassA>& foo(const std::string& key) {
    std::map<std::string, std::set<ClassA>>::iterator itr = m_elements.find(key);
    return (itr != m_elements.end()) ? *itr : cNull<std::set<ClassA> >.Value();
}

私が疑問に思っていたのは、この問題を解決するためのより良い方法があるかどうか、およびこの設計に問題があるかどうかです。

4

5 に答える 5

1

私が疑問に思っていたのは、この問題を解決するためのより良い方法があるかどうか、およびこの設計に問題があるかどうかです。

確かに: 参照ではなくコピーを返します。「空を返すことができるようにしたい」と言うとき、std::set<T>元の状態に関して返される値を(メンバー変数として)変更したいかもしれないと言っているだけです。この場合、コピーはまったく問題ありません。

于 2013-11-22T15:47:53.550 に答える
1

このような状況では、通常、次の 2 つの選択肢があります。

  • 要素が見つからない場合は、例外をスローします。
  • または、見つかった要素を含む可能性boost::optionalのあるようなオブジェクトを返します。
于 2013-11-22T15:37:41.560 に答える
1

参照を返す理由によって少し異なります。

呼び出し元が参照を保持し、呼び出されたオブジェクトを介して行われた変更を反映させることを期待しているfoo場合、シングルトンの空のセットへの参照を返し、同じキーが後でm_elements? 参照は、宣伝されていることを実行しません。が呼び出されたときに空のセットをマップに追加する方がよい場合fooがあるため、コードは次のようになります。

const std::set<int>& foo(const std::string& key) {
    return m_elements[key];
}

オブジェクトの一部への参照のセマンティクスが実際に必要なためではなく、パフォーマンス上の理由のみで参照を返す場合 (大規模なセットの潜在的に高価なコピーを回避するため)、静的な空のセットを返すとうまくいきます。そして、複数の型で同じことを頻繁に行っていることに気付いた場合、それを達成するためのテンプレート ヘルパーを用意しても問題はないと思います。ただし、返された参照がオブジェクトへのさらなる変更を反映している場合と反映していない場合があることを文書化するように十分に注意してください( が呼び出されkeyたときに存在していたかどうかによってfoo、保証したくない場合があります)。そうすれば、呼び出し元は、販売期限を過ぎては使用しないようにする必要があることがわかります。販売期限は、誰かが次にオブジェクトに関連する変更を加えたときです。

参照を返す理由がわからない場合は、コピーを返すか、呼び出し元がこのセットで何をすることが期待されているかを調べて、それを行うfoo1 つ以上の関数に置き換えます。そうすれば、クラスの内部への参照がユーザーの手に渡ることはありません。

空集合は何らかの正当な理由で正しいと仮定しました。おそらく、すべての呼び出し元が戻り値をテストしたり、key呼び出す前にの存在を確認したりする必要がないようにするためfooです。

于 2013-11-22T16:11:29.590 に答える
0

ポインターまたは nullptr を返し、not foundを示します。

const std::set<ClassA>* foo(const std::string& key) 
{
    auto it = m_elements.find(key);
    if (it == m_elements.end()) return NULL;
    return &(*it);
}

シンプルで、シングルトン炎に悩まされることはありません。

于 2013-11-22T17:57:02.303 に答える
0

これを行うにはいくつかのオプションがありますが、1 つを選択する前に、次の質問に答える必要があります。本当に参照を返す必要がありますか?

結果を操作しようとしている (そしてそれを変更に反映させたい - それ以外の場合はインターフェイスを変更できない) 場合、答えはイエスです。これらの条件のいずれかが変更された場合は、コピーで戻ることができます。これにより、これがはるかに簡単になります。

std::set<int> foo(const std::string& key) const
{
    std::set<int> results;
    std::map<std::string, std::set<int>>::iterator it = m_elements.find(key); // assuming m_elements is std::map<std::string, std::set<int>>
    if (it != m_elements.end())
    {
        results = it->second;
    }
    return results;
}

参照を返す必要がある場合は、次のようなことを行ったりboost::optional(またはstd::pairそれをシミュレートするために使用したり)、例外をスローしたり、内部に null を設定したりできます。あなたがやろうとしていることに最も近いオプションは、オプション/ペアのアプローチです:

std::pair<bool, std::set<int>*> foo(const std::string& key)
{
    std::pair<bool, std::set<int>*> results = std::make_pair(false, nullptr);
    std::map<std::string, std::set<int>>::iterator it = m_elements.find(key);
    if (it != m_elements.end())
    {
        results.first = true;
        results.second = &(it->second);
    }
    return results;    
}

次に、ブール値 (有効であることが保証されている) をチェックして、ポインター値が有効かどうかを確認できます。 boost::optional同様のことを行います(ブーストを使用できる場合は、std::pairバージョンを使用してシミュレートするよりも優先します)。

于 2013-11-22T15:50:00.360 に答える