定义标准C ++类型的NULL / Empty值

时间:2013-11-22 15:33:31

标签: c++ stl const-reference

我的类有返回const <Container>&的方法,因为我不希望在外面修改返回的容器,并且复制可能很昂贵。例如:const std::set<ClassA>& foo()我希望foo()能够将const引用返回到空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>中真正返回对临时构造的空foo()的const引用。为了解决这个问题,我在一个公共场所定义了一个通用模板单例类,以便它可以用于任何类型

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();
}

我想知道的是,如果有更好的方法来解决这个问题,并且这个设计是否有任何问题?

5 个答案:

答案 0 :(得分:1)

在这种情况下,你通常两种选择:

  • 如果没有找到任何元素,则抛出异常。
  • 或者返回一些像boost::optional这样的对象,可能包含找到的元素。

答案 1 :(得分:1)

  

我想知道的是,如果有更好的方法来解决这个问题,并且这个设计是否有任何问题?

当然有:不要返回引用而是复制。当您说“您希望能够返回空std::set<T>”时,您只是声明您可能想要更改返回的值,而不是原始状态(作为成员变量)。在这种情况下,副本完全没问题。

答案 2 :(得分:1)

取决于你为何返回参考资料。

如果是因为调用者希望保留引用并使其反映通过调用foo的对象所做的更改,那么如果返回对单例空集的引用会发生什么,并且相同密钥后来添加到m_elements?该引用不会做它所宣传的内容。在调用foo时向地图添加空集可能更好,因此代码变为:

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

如果您仅出于性能原因返回引用(以避免大型集合的潜在昂贵副本),而不是因为您实际上需要对对象的一部分的引用的语义,那么返回静态空集如果你发现自己经常用几种类型做同样的事情,我认为有一个模板帮助器来实现它是没有错的。但是要非常小心地记录,返回的引用可能会或可能不会反映对象的进一步更改(根据调用key时是否存在foo,尽管你可能不想保证这一点。然后,呼叫者将知道在其销售日期之后避免使用它,即每当有人对该对象进行相关更改时。

如果你不知道为什么要返回一个引用,那么要么返回一个副本,要么计算出调用者应该用这个的内容,并替换{{ 1}}有一个或多个函数来完成它。这样,您就不会允许对您班级内部的引用落入用户手中。

我认为空集是正确的,原因很简单 - 可能是为了避免每个调用者在调用foo之前必须测试返回值和/或检查是否存在key

答案 3 :(得分:0)

有几个选项可以做到这一点,但在你选择之前,你必须回答:我真的需要回复参考吗?

如果您正在尝试对结果进行操作(并希望它反映在更改中 - 并且无法以其他方式更改界面),答案是肯定的。如果这些条件中的任何一个发生变化,您可以通过复制返回,这使得这更容易:

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来模拟它),抛出异常,具有内部空值。与您尝试执行的操作最接近的选项是可选/配对方法:

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执行类似的操作(如果您可以使用boost,请使用std::pair版本来模拟它)。

答案 4 :(得分: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);
}

简单,并且不会患单神经炎。