(可能与 How to implement a C++ method that creates a new object, and returns a reference to it 有关,后者是关于一些不同的东西,但附带地包含几乎完全相同的代码)
我想从静态函数返回对静态本地的引用。当然,我可以让它工作,但它没有我想要的那么漂亮。
这可以改进吗?
背景
我有几个类,它们除了以明确定义的方式可靠地获取或初始化资源并释放资源外,并没有做太多事情。他们甚至不需要对资源本身了解太多,但用户可能仍想以某种方式查询一些信息。
这当然是微不足道的:
struct foo { foo() { /* acquire */ } ~foo(){ /* release */ } };
int main()
{
foo foo_object;
// do stuff
}
微不足道。或者,这也可以正常工作:
#include <scopeguard.h>
int main
{
auto g = make_guard([](){ /* blah */}, [](){ /* un-blah */ });
}
除了现在,查询东西有点困难,而且没有我喜欢的那么漂亮。如果您更喜欢 Stroustrup 而不是 Alexandrescu,则可以改为包含 GSL 并使用一些涉及 final_act
的混合物。随便。
理想情况下,我想写这样的东西:
int main()
{
auto blah = foo::init();
}
如果你想这样做,你可以从哪里得到一个对象的引用,你可以查询它。或者忽略它,或者其他什么。我的直接想法是:简单,这只是伪装的 Meyer 单例。因此:
struct foo
{
//...
static bar& init() { static bar b; return b; }
};
就是这样!死的简单,完美。 foo
是在您调用 init
时创建的,您会得到一个可以查询统计信息的 bar
,它是一个引用,所以您不是所有者,foo
最后会自动清理。
除了...
问题
当然 不可能 这么简单,任何使用过 range-based for 和 auto
的人都知道你必须写 auto&
如果你不想要惊喜拷贝。但是,唉,单独的 auto
看起来非常无辜,我没有想到它。另外,我明确地返回了一个引用,所以 auto
可能捕获的只是一个引用!
结果:创建了一个拷贝(从什么?大概是从返回的引用?)当然有一个范围内的生命周期。默认复制构造函数被调用(无害,什么都不做),最终复制超出范围,并且上下文在操作中途被释放,东西停止工作。在程序结束时,再次调用析构函数。咔嚓咔嚓。咦,怎么会这样。
显而易见的(好吧,在第一秒不是那么明显!)解决方案是写:
auto& blah = foo::init();
这行得通,而且工作正常。问题解决了,除了......除了它不漂亮而且人们可能会像我一样不小心做错了。难道我们不需要额外的符号就可以吗?
返回 shared_ptr
也可能有效,但这会涉及不必要的动态内存分配,更糟糕的是,在我看来这将是“错误的”。您不分享所有权,您只被允许查看别人拥有的东西。原始指针?正确的语义,但是......呃。
通过删除复制构造函数,我可以防止无辜用户陷入 forgot-& 陷阱(这将导致编译器错误)。
然而,这仍然没有我想要的那么漂亮。必须有一种方式将“此返回值作为引用” 传达给编译器吗?类似于 return std::as_reference(b);
?
我曾想过一些涉及“移动”对象而不真正移动它的骗局,但不仅编译器几乎肯定不会让你移动静态本地,而且如果你设法做到了,你有更改所有权,或使用“假 Action ”移动构造函数再次调用析构函数两次。所以这不是解决方案。
有没有更好、更漂亮的方法,还是我只能忍受编写 auto&
?
最佳答案
Something like return std::as_reference(b);?
你的意思是像 std::ref
?这将返回 std::reference_wrapper<T>
您提供的值(value)。
static std::reference_wrapper<bar> init() { static bar b; return std::ref(b); }
当然,auto
将返回的类型推断为 reference_wrapper<T>
而不是 T&
.同时 reference_wrapper<T>
有一个隐含的 operatorT&
,这并不意味着用户可以完全像引用一样使用它。要访问成员,他们必须使用 ->
或 .get()
.
尽管如此,我相信您的想法是错误的。原因是 auto
和 auto&
每个 C++ 程序员都需要学习如何处理。人们不会让他们的迭代器类型返回 reference_wrapper
s 而不是 T&
.人们通常不使用 reference_wrapper
完全以这种方式。
因此,即使您像这样包装所有界面,用户仍然必须最终知道何时使用 auto&
.因此,实际上,除了您的特定 API 之外,用户没有获得任何安全。
关于c++ - 提示编译器返回一个使 'auto' 表现良好的引用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39350168/