从概念上讲,我有一门课可以做到这一点。请注意,底层数据类型在实际应用中更为复杂。这只是为了简化:
class A
{
private:
std::map<std::string, int> database;
public:
bool knowsValue(std::string string_id); // Returns true if string_id is in database
const int& getValueA(std::string string_id); // Returns reference to int mapped to string_id in database
}
由于为未知的string_id
调用getValueA会导致错误,因此通常会同时调用两个命令:
if obj.knowsValue(string_id)
int val = obj.getValueA(string_id)
else
std::cout << "Value does not exist in database";
因为这需要对数据库对象执行两个后续的find
操作,所以我做了此函数bool getValueB(std::string string_id, int& val)
,如果string_id
在数据库中,则该函数返回true,并将映射值分配给{ {1}}。
两个val
函数的内容几乎相同,因此我想重用getValue
中的getValueB
。通过这种尝试,这就是我无法做到的地方:
getValueA
显然,const int& getValueA2(std::string string_id)
{
static int val_tmp; // If not static, this object is deleted after the function call and the reference becomes invalid
if (getValueB(string_id, val_tmp))
return static_cast<const int&>(val_tmp);
else
return static_cast<const int&>(0);
}
关键字在此处不合适,因为不应在函数之间共享该值。同样,引用不是static
中的const
的事实也不是最优的。
我的问题:
getValueB
(试图返回它在参数中获得的引用)的正确方法是什么?中间的getValueA2
似乎很邪恶。val_tmp
吗?我倾向于将const
更改为getValueB
来消除这种混乱,但是我将对找出可能的情况以及出了问题的地方感兴趣。
edit:请注意,const int& getValueB(std::string string_id, const bool value_exists_in_db)
的声明最好不要更改,以免更改其余代码库。
答案 0 :(得分:3)
我认为,从根本上讲,您不应该尝试将两者结合起来,因为它们实际上在做完全不同的事情。
一个参数版本(const int& getValueA(const std::string&)
)返回对某个值的引用。
两个自变量版本(bool getValueA2(const std::string&, int&)
)将值的副本分配给调用方提供的新位置。
您在这里有几个选择:
bool getValueA2(const std::string &, const int *&)
的形式,以巩固功能。这很丑陋,而且基本上没有吸引力。如果您去#4,它可能看起来像这样(请注意,由于您没有提供足够的详细信息让我这样做,因此我无法提供更相关的示例):
auto getValueAImpl(const std::string &key) const {
return database.find(key);
}
const int &getValueA(const std::string &key) {
auto it = getValueAImpl(key);
if (it != database.end()) return it->second;
throw std::runtime_error("key not found");
}
bool getValueA(const std::string &key, int &val) {
auto it = getValueAImpl(key);
if (it == database.end()) return false;
val = it->second;
return true;
}
答案 1 :(得分:2)
如果您可以选择使用C ++ 17,我建议您使用std::optional<int>
作为返回类型。
std::optional<int> getValueA2(std::string string_id)
{
auto iter = database.find(id);
// If found, return the value.
if ( iter != database.end() )
{
return {iter->second}; // Equivalent to std::optional<int>{iter->second};
}
else
{
// Not found. Return a default constructed object
return {};
}
}
如果您不能使用C ++ 17,则可以使用std::pair<bool, int>
作为返回类型,以穷人代替std::optional<int>
。
如果找到该值,则返回{true, iter->second}
。
如果找不到该值,则返回{false, 0}
。
答案 2 :(得分:2)
通过const引用返回int
是一种悲观。 int
s复制很便宜;比引用所依据的指针便宜或便宜。出于这个原因,我想说的正确做法是将getValueA
更改为按值返回,然后您可以根据getValueB
来实现它:
int getValueA(const std::string& string_id)
{
int val_tmp;
if (getValueB(string_id, val_tmp))
return val_tmp;
else
return 0;
}
另一种选择是将两者合并,然后返回std::optional<int>
:
std::optional<int> getValue(const std::string& string_id)
{
auto it = database.find(string_id);
if (it == database.end()) {
return std::nullopt;
} else {
return *it;
}
}
注意:我也将参数类型更改为const std::string&
。复制字符串可能会很昂贵,因此在这种情况下使用引用很有意义。
答案 3 :(得分:1)
const int* getValuePtr(std::string const& id) const
{
auto it = database.find(id);
if(it==database.end()) return nullptr;
return &(it->second);
}
指针是一个可能的引用。
使用时:
if(auto x=foo.getValuePtr("bob")){
// use *x here safely
}
答案 4 :(得分:1)
如果您的函数返回了引用,则该函数需要确保返回的引用用于调用方使用它时仍然存在的内容。
在您的示例中;
const int& getValueA2(std::string string_id) { static int val_tmp; // If not static, this object is deleted after the function call and the reference becomes invalid if (getValueB(string_id, val_tmp)) return static_cast<const int&>(val_tmp); else return static_cast<const int&>(0); }
然后,val_tmp
必须是您所拥有的static
,或者是以某种方式分配的,以便它继续存在。如果调用者使用返回的引用,则删除static
关键字将导致调用者具有未定义的行为-因为就您的程序而言,当函数返回时,val_tmp
将不复存在。
由于您希望函数以某种方式获取值,因此更好的选择是按值而不是引用返回。
int getValueA2(std::string string_id)
{
int val_tmp;
if (getValueB(string_id, val_tmp))
return val_tmp;
else
return 0;
}
这很好,假设getValueB()
通过引用接受val_tmp
,并使用该引用将有效值分配给变量。