我有一个Maybe
class 是可能包含给定类型的基于堆栈的类。我们有某些函数返回 Maybe
包含可变或常量引用。这主要是为了减少样板文件、查找和不需要的拷贝。
Map<String, Foo> map;
// Normal C++
auto it = map.find("foo");
if (it != map.end())
doStuff(*it);
// Has an extra lookup, bad
if (map.contains("foo"))
doStuff(map.get("foo"));
// Uses Maybe
if (auto val = map.maybe("foo"))
doStuff(*val);
// Also possible:
// apply calls the function with *this as argument if this is valid
map.maybe("foo").apply(&doStuff);
但是,当 map
时,这是有问题的是临时的:
Map<String, Foo> map;
Map<String, Foo> getMap() { return map; } // Returns a copy of map
if (auto val = getMap().maybe("foo")) // Returns Maybe<Foo&> to temporary
doStuff(*val); // Very bad, *val has already been deleted
另一方面,因为Maybe<Foo>
可从 Maybe<Foo&>
构造(一般来说, Maybe<T2>
可以从 Maybe<T>
构造,如果 T2
可以从 T
构造)那么如果我写这个,这不是问题。
if (Maybe<Foo> val = getMap().maybe("foo"))
doStuff(*val); // OK, val contains a copy
在一位同事偶然发现这个问题后,我想到了一个好主意,在可能返回 Maybe<T&>
的地方使用 ref 限定的成员函数。返回 Maybe<T>
相反,如果它是右值。
Maybe<Val> Map<Key, Val>::maybe(Key const& key) &&;
Maybe<Val const&> Map<Key, Val>::maybe(Key const& key) const&;
Maybe<Val &> Map<Key, Val>::maybe(Key const& key) &;
但是我在弄清楚在 const&&;
的情况下该怎么做时遇到了一些困难
如果没有,它将调用 const&
版本,这很糟糕,因为这会返回一个引用。
我考虑制作 &&
版本a const&&
版本以避免重复;但是,那么我需要复制而不是移动内部值。而且我对 const&&
的语义理解不够了解 const_cast
是否可以接受在内部,或者当我变异 const&&
时这会导致疯狂和歇斯底里。如果不需要的话,我宁愿不必编写该函数的两个拷贝。
在这种情况下,正常的最佳实践是什么?我需要编写全部 4 个函数吗?或者我可以理智地摆脱 3 吗?
是否有更好的方法来避免这种悬空引用问题?这只是一个问题,因为 auto
通常会删除引用,因此您不会意外地引用临时对象,但因为 Maybe
的类型已经是一个普通值,它只是包装了一个引用类型,这就有可能搬起石头砸自己的脚。只是说“那么在这种情况下不要使用 auto
”是非常诱人的,但仍然很容易不小心搞砸,而且我宁愿很难做错事。
最佳答案
无论如何,你不可能在好的代码中得到 const&&
。
唯一的方法是
- 调用返回
const T
的函数。 (无论如何,拥有这样的返回类型都是一个坏主意。) - 调用返回
const T&&
的函数。 (迪托。) - 故意转换为
const&&
。 (无论如何你都不会这样做。) - 来自
&&
的隐式转换。 (这不会发生,因为你有一个接受右值引用的重载。)
因此,如果您想要让 API 防弹,正确的方法就是删除
该重载:
Maybe<Val &> maybe(Key const& key) const&& = delete;
关于c++ - Ref 限定的成员函数设计打破了 const 右值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27973279/