我的类有返回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();
}
我想知道的是,如果有更好的方法来解决这个问题,并且这个设计是否有任何问题?
答案 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);
}
简单,并且不会患单神经炎。