ref-qualified成员函数设计了const rvalues的中断

时间:2015-01-15 21:26:53

标签: c++ c++11 reference

我有一个Maybe类,它是一个基于堆栈的类,可能包含给定的类型。我们有某些函数返回包含mutable或const引用的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 {1}})如果我写这个,那不是问题。

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&&是否可以接受,或者当我改变auto时,这会导致疯狂和歇斯底里。如果我不必,我不必写这个功能的两个副本。

在这种情况下,什么是正常的最佳做法?我需要编写所有4个函数吗?或者我可以用3来轻松逃脱?

有没有更好的方法来避免这种悬空参考问题?这只是一个问题,因为Maybe通常会删除引用,因此您不会意外地引用临时引用,但因为auto的类型已经是一个普通值,所以#&} 39;只是包装一个参考类型,就可以用脚射击自己了。只是说&#34;那么在那种情况下不要使用{{1}},&#34;这是非常诱人的,但它仍然很容易意外搞砸,而且我很难做错事。

2 个答案:

答案 0 :(得分:2)

无论如何,你都无法获得良好代码的const&&

唯一的方法是

  1. 调用返回const T的函数。 (无论如何,拥有这样的回归类型是一个坏主意。)
  2. 调用返回const T&&的函数。 (DITO)
  3. 故意转向const&&。 (无论如何你都不会这样做。)
  4. &&的隐式转换。 (这不会发生,因为你有一个超载接受r值参考。)
  5. 因此,如果您想要对API进行防弹,那么正确的方法只是delete - 过载:

    Maybe<Val &> maybe(Key const& key) const&& = delete;
    

答案 1 :(得分:0)

我认为这不是一个真正的问题。考虑一下程序

#include <map>
#include <cstdio>
int main() {
  int& x = std::map<int, int>{{3, 4}}[3];
  printf("%d\n", x);
}

在地图被销毁之后,引用x将悬空(使最后一行未定义行为)。标准库没有阻止这种情况。

我也从未听说过有人意外犯过这种错误。

使用您的地图,情况相同。

IMO,根据地图的值类别返回Maybe<Val>Maybe<Val&>太混乱了。每次在临时对象上调用.maybe时,请三思而后行。