如何返回不可用的常量引用
How to return a const reference which is not available
假设我有以下函数:
const std::string& Cat::getKittenName() const
{
Kitten* kitty = getKitty();
return kitty->getName();
}
Kitten::getName
返回const std::string&
我如何最好地处理kitty
是nullptr
的情况?我可以返回std::string("")
但随后我返回对临时且实际上保证未定义行为的引用。我可以更改getKittenName
函数以返回一个std::string
来解决此问题,但随后我为所有可用的kitty
情况引入了冗余副本。现在我觉得最好的选择是:
const std::string& Cat::getKittenName() const
{
Kitten* kitty = getKitty();
if (kitty)
{
return kitty->getName();
}
static std::string empty("");
return empty;
}
唯一的问题可能是"魔术静态"不可用。此解决方案是否有任何问题,或者有更好的方法吗?
你有几个选择,真的。
-
最简单的方法是返回
std::string
,但您提到出于性能原因不希望这样做。我想说你应该首先分析以确保它会出现明显的性能问题,因为所有其他解决方案都会使代码更复杂,因此至少更难维护。但让我们说它看起来确实很重要。 -
如果您担心线程安全的函数范围静态未实现,则可以创建回退值作为
Cat
的静态成员:class Cat { static const std::string missingKittenName; public: const std::string& Cat::getKittenName() const { Kitten* kitty = getKitty(); if (kitty) return kitty->getName(); else return missingKittenName; } };
-
由于
Kitten::getName()
显然返回了一个引用(否则你不会担心副本),你也可以返回一个指针:const std::string* Cat::getKittenName() const { Kitten* kitty = getKitty(); if (kitty) return &kitty->getName(); else return nullptr; }
-
您可以返回对字符串的可选引用:
boost::optional<const std::string&> Cat::getKittenName() const { Kitten* kitty = getKitty(); if (kitty) return kitty->getName(); else return boost::none; }
从 C++17 开始,
optional
作为std::optional
成为标准库的一部分,因此不再需要回退到 Boost。 -
如果缺少名称的事实属于异常情况(错误),则可以引发异常:
const std::string& Cat::getKittenName() const { Kitten* kitty = getKitty(); if (kitty) return kitty->getName(); else throw std::invalid_argument("Missing kitten"); }
返回对 const 静态 std::string 的引用。
原因:
- "魔术静力学"不是魔术,它们是C++标准的一部分。
- 静态是在代码第一次流过它们时构造的 (即曾经一次)
- 从 C++11 开始,静态构造是线程安全的。
- 静态对象以正确的顺序正确释放,位于 节目结束
- 一个冗余静态对象的性能损失完全可以忽略不计,并且远低于测试返回的 NULL 指针的成本。
如果在 c++11 之前的编译器上是多线程的,则需要编写线程安全的单一实例来制造默认字符串,或在文件范围内定义它。
C++11:
const std::string& Cat::getKittenName() const
{
static const std::string noname { /* empty string */ };
Kitten* kitty = getKitty();
if (kitty)
{
return kitty->getName();
}
return noname;
}
C++03:
namespace {
const std::string noname;
}
const std::string& Cat::getKittenName() const
{
Kitten* kitty = getKitty();
if (kitty)
{
return kitty->getName();
}
return noname;
}
返回一个指示成功/失败的布尔值,并通过指针传递输出。
下面是一个示例:
bool Cat::getKittenName(std::string *name) const {
Kitten* kitty = getKitty();
if (kitty) {
*name = kitty->getName();
return true;
}
return false;
}
您将按如下方式使用它:
std::string name;
if(mycat.getKittenName(&name)) {
// name is populated and is valid
}
这要求指针传入的类具有有效的复制构造函数和复制赋值运算符。
我更喜欢这样做有几个原因:
- 返回指针令人困惑,调用方不清楚如何处理指针。
例如上面列出的示例:
const std::string* Cat::getKittenName() const
{
Kitten* kitty = getKitty();
if (kitty)
return &kitty->getName();
else
return nullptr;
}
会这样称呼:
const std::string *name = mykitty.getKittenName();
好吧,现在怎么办? 我不能使用此指针更改 name 的值,但我可以更改指针指向的内容吗?我应该把它包装在一个
std::unique_ptr<const std::string>
??一般来说,返回指针是非常C式的,除了令人困惑之外,还是内存泄漏和错误的重要来源。
- 引发异常
此示例:
const std::string& Cat::getKittenName() const
{
Kitten* kitty = getKitty();
if (kitty)
return kitty->getName();
else
throw std::invalid_argument("Missing kitten");
}
我不喜欢,因为它鼓励为非特殊行为抛出例外。 例如,如果您在一组小猫上编写了一个名为"find()"的方法。找不到特定名字的小猫并不是"例外"。STL 使用各种机制(如 std::end 和 std::p air )来避免为非异常行为引发异常。
由此产生的另一个问题是,与Java不同,它并不完全清楚代码中抛出异常的位置,并且如果您没有意识到某些内容会抛出某些内容,则传递调用将无法正确清理。 这在引发异常的库中尤其邪恶。
我更喜欢以下风格:
retval method(const input&, pointer *output) { }
因为输入和输出是如何放置在方法上的很清楚的。 这也避免了必须返回时髦的结构,如 std::p air,它无法扩展。
可能最正确的方法是这样的:
#include <optional>
#include <functional>
std::optional<std::reference_wrapper<const std::string>> Cat::getKittenName() const
{
Kitten* kitty = getKitty();
if (kitty)
{
return kitty->getName();
}
return std::nullopt;
}
下面是一个完整的演示:
#include <optional>
#include <functional>
std::optional<std::reference_wrapper<const int>> foo() {
static int a = 5;
if (a > 5) {
return a;
}
return std::nullopt;
}
int bar() {
auto x = foo();
if (x.has_value()) {
return *x;
}
return 0;
}
- 什么时候在C++中返回常量引用是个好主意
- 通过常量引用传递参数的矩阵模板类
- 在C++中使用非常量引用作为常量
- 具有常量引用参数的函数模板专用化
- 多个"常量引用"变量可以共享同一个内存吗?
- 为什么 STL 容器适配器堆栈中的 top 返回常量引用?
- 为什么常量方法可以采用非常量引用?
- 为什么当我们有常量引用时创建临时对象?
- 如何返回向量的常量引用?
- C++:常量引用参数
- 不同于按值传递和常量引用传递的程序集
- 为什么const_cast和static_cast常量引用没有效果?
- C++ 获取函数在常量引用中按值返回的结果
- 从 BubbleSort* 类型的右值初始化 'AssortedSorter&' 类型的非常量引用无效"
- C++ 在类中使用常量引用文本时 O2 内存泄漏
- 是否可以跨 dll 边界返回常量引用/指向 std::vectors?
- C++中大多数/所有 setter 函数的参数是否应该写为常量引用?
- 通过非常量引用参数修改常量引用参数
- 将常量引用传递给线程
- 为什么C++中没有常量引用,就像常量指针一样?