引用限定的成员函数设计中断常量右值
Ref-qualified member functions design breaks on const rvalues
我有一个Maybe
类,它是一个基于堆栈的类,可能包含给定的类型。 我们有一些函数返回一个包含可变或常量引用的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&>
的地方使用限定的成员函数来返回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&
版本,这很糟糕,因为这会返回引用。
我考虑过将&&
版本设为const&&
版本以避免重复;但是,我需要复制,而不是移动内部值。 而且我对const&&
的语义了解不够,不知道内部const_cast
是否可以接受,或者这是否会导致疯狂和歇斯底里,而我突变了const&&
。 如果没有必要,我宁愿不必编写此函数的两个副本。
在这种情况下,什么是正常的最佳实践? 我需要编写所有 4 个函数吗? 或者我可以理智地摆脱 3 吗?
有没有更好的方法来避免这种悬而未决的参考问题? 这只是一个问题,因为auto
通常会剥离引用,因此您不会意外地将引用带到临时值,但是因为Maybe
的类型已经是一个普通值,它只是包装引用类型,有可能搬起石头砸自己的脚。 只是说"那么在这种情况下不要使用auto
"是非常诱人的,但仍然很容易不小心搞砸,我宁愿做错事很难。
你都不可能得到一个好的代码const&&
。
唯一的方法是
- 调用返回
const T
的函数。(无论如何,拥有这样的返回类型是一个坏主意。 - 调用返回
const T&&
的函数。(迪托。 - 故意投射到
const&&
.(反正你不会这样做。 - 从
&&
隐式转换。(这不会发生,因为您有一个通过 r 值引用接受的重载。
因此,如果你想让你的API防弹,正确的方法就是delete
重载:
Maybe<Val &> maybe(Key const& key) const&& = delete;
这不是一个真正的问题。考虑该计划
#include <map>
#include <cstdio>
int main() {
int& x = std::map<int, int>{{3, 4}}[3];
printf("%dn", x);
}
地图销毁后,参考x
将悬而未决(使最后一行的行为未定义)。标准库不采取任何措施来防止这种情况。
我也从未听说过有人不小心犯了这种错误。
对于您的地图,情况相同。
IMO,根据地图的值类别返回Maybe<Val>
或Maybe<Val&>
太混乱了。每次在临时对象上调用.maybe
时,请三思而后行。
- #定义c-预处理器常量..我做错了什么
- 用C++中的一个变量定义一个常量
- 什么时候在C++中返回常量引用是个好主意
- 代理对象的常量正确性
- 我想将一个对T类型的非常量左值引用绑定到一个T类型的临时值
- 通过多个头文件使用常量变量
- 在cuda线程之间共享大量常量数据
- 不能在初始值设定项列表中将非常量表达式从类型 'int' 缩小到'unsigned long long'
- 有没有什么方法可以使用一个函数中定义的常量变量,也可以由c++中同一程序中的其他函数使用
- 是默认情况下分配给char数组常量的值
- 私有类型的静态常量成员
- 类似枚举的计算常量
- 递归模板化函数不能分配给具有常量限定类型"const tt &"的变量"state"
- 为什么我可以通过引用修改常量返回
- 如何创建长度由常量参数指定的数组
- 当一个值是非常量但用常量表达式初始化时使用constexpr
- 返回常量对象引用 (getter) 和仅返回字符串有什么区别?
- 从函数返回类型中删除常量是否会中断 ABI
- 引用限定的成员函数设计中断常量右值
- 通过中断修改常量引用