c++ - 提示编译器返回一个使 'auto' 表现良好的引用

标签 c++ c++14

(可能与 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() .

尽管如此,我相信您的想法是错误的。原因是 autoauto& 每个 C++ 程序员都需要学习如何处理。人们不会让他们的迭代器类型返回 reference_wrapper s 而不是 T& .人们通常不使用 reference_wrapper完全以这种方式。

因此,即使您像这样包装所有界面,用户仍然必须最终知道何时使用 auto& .因此,实际上,除了您的特定 API 之外,用户没有获得任何安全。

关于c++ - 提示编译器返回一个使 'auto' 表现良好的引用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39350168/

相关文章:

c++ - 抛出一个右值

c++ - 使用非默认可构造类型填充 std::array(无可变参数模板)

c++ - 转置模板参数包

c++ - XmlHttpRequest 错误?

c++ - 发射信号和槽的信息和控制(Qt)

c++ - 在远程机器上运行线程

C++/过剩 : Generating multiple asteroids/bullets

c++ - 在 C++14 中将 constexpr intializer_list 作为参数传递

c++ - 递增 std::string::end() 迭代器是否未定义?

C++ 使用函数参数类型进行模板参数推导