我们可以使用nodiscard
属性来暗示函数的返回值不应被丢弃。是否有任何属性(或其他方式)来暗示一些相反语义:函数的返回值只能临时使用(我的意思是“临时”,不分配给除本地变量之外的任何变量)?
由于目的可能不会立即明确,请考虑我有一个类 FooHolder
来保存资源 Foo
;调用 FooHolder::getFoo()
返回当前持有的 Foo
:
#include <memory>
class Foo {
public:
Foo& bar() { /* do something */ return *this; }
const Foo& far() const { /* do something else */ return *this; }
};
class FooHolder {
private:
std::shared_ptr<Foo> _foo { nullptr };
public:
FooHolder(): _foo(std::make_shared<Foo>()) {}
Foo& getFoo() { return *_foo; }
const Foo& getFoo() const { return *_foo; }
};
我们可以通过多种方式使用它:
// Others may try storing some status:
Foo* g_foo = nullptr;
int main() {
FooHolder foo_holder {};
// I want to support this:
foo_holder.getFoo().bar().far() /* chained calls... */ ;
// Also, maybe this:
auto& local_foo = foo_holder.getFoo();
local_foo.bar();
local_foo.far();
// But not this, because the Foo instance that FooHolder holds may perish:
static Foo& static_foo = foo_holder.getFoo();
// Nor this:
g_foo = &local_foo;
return 0;
}
那么有没有办法阻止(或至少警告)存储 FooHolder::getFoo()
的返回值?或者,通过引用返回资源是不好的做法吗?
最佳答案
[...] is it bad practice to return resources by reference?
这要看情况。有很多返回非常量引用的方法的示例,它们都很好。例如,考虑标准容器元素访问器。然而,它们并不用于封装。 std::vector::operator[]
并不是要向调用者隐藏元素,而是要提供对其的直接访问。返回非常量引用不是封装!事实恰恰相反。请注意,std::vector
甚至授予您对其 data()
的访问权限。这也不是封装,它依赖于用户不delete[] some_vect.data()
或做其他会破坏 vector 的错误事情。
您希望 FooHolder
封装所包含的 Foo
。
这是相反的要求。
您基本上有两个选择:A) 调用者知道他们在做什么。他们阅读文档。他们知道不应该以错误的方式使用 FooHolder::getFoo
。 B) 使用适当的封装:永远不要让调用者直接访问非常量 Foo
:
class FooHolder {
private:
std::shared_ptr<Foo> _foo { nullptr };
public:
FooHolder(): _foo(std::make_shared<Foo>()) {}
// nope // Foo& getFoo() { return *_foo; }
// maybe // const Foo& getFoo() const { return *_foo; }
FooHolder& bar() {
_foo->bar();
return *this;
}
// ..same for far() ...
};
请注意,A) 是一个可行的解决方案。考虑一下像 std::shared_ptr 这样的东西也可能被错误地使用。用户应该知道如何正确使用它。不同之处在于 std::shared_ptr 是一个标准类型,具有大量文档。因此,如果这是正确的选择,您应该三思而后行。
So are there ways to prevent (or at least warn about) storing the return value of FooHolder::getFoo()?
没有。一旦你返回了一个非常量引用,所有的赌注都会被取消。 FooHolder
不再控制调用者可以使用该引用执行哪些操作。您可以阻止复制或移动,但无法阻止保留引用。
关于c++ - 暗示返回值不被存储的方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/71077172/