【问题标题】:Ref-qualified member functions design breaks on const rvalues引用限定的成员函数设计在 const rvalues 上中断
【发布时间】:2015-01-15 21:26:53
【问题描述】:

我有一个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&lt;Foo&gt; 可以从Maybe&lt;Foo&amp;&gt; 构造(通常,Maybe&lt;T2&gt; 可以从Maybe&lt;T&gt; 构造,如果T2 可以从T 构造)那么如果我写这个,它不是问题。

if (Maybe<Foo> val = getMap().maybe("foo"))
  doStuff(*val); // OK, val contains a copy

在一位同事偶然发现这个问题后,我有了一个绝妙的主意,即在可能返回 Maybe&lt;T&amp;&gt; 的地方使用 ref 限定的成员函数,而不是返回 Maybe&lt;T&gt;,如果它是一个右值。

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&amp;&amp;;,我很难弄清楚该怎么做

如果没有,它将调用const&amp; 版本,这很糟糕,因为这会返回一个引用。

我考虑将&amp;&amp; 版本设为const&amp;&amp; 版本以避免重复;但是,那么我需要复制,而不是移动内部值。而且我对const&amp;&amp; 的语义还不够了解,无法知道const_cast 在内部是否可以接受,或者当我对const&amp;&amp; 进行变异时,这是否会导致疯狂和歇斯底里。如果不需要,我宁愿不必编写此函数的两个副本。

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

有没有更好的方法来避免这个悬空引用问题?这只是一个问题,因为auto 通常会剥离引用,所以你不会意外地引用临时的,但是因为Maybe 的类型已经是一个普通值,它只是包装了一个引用类型,它有可能射中自己的脚。只是说“那么在那种情况下不要使用auto”很诱人,但仍然很容易不小心搞砸,我宁愿很难做错事。

【问题讨论】:

  • 这个问题可能有太多的上下文,但我试图避免出现 XY 问题场景。
  • 你有什么理由不只是deleteconst&amp;&amp;-variant?我的意思是,这似乎是病态的。另外,我不确定如何在不故意转换为 const&amp;&amp; 或从其他品种之一隐式转换的情况下获得 const&amp;&amp;...
  • 我并没有真正想到 delete 它,但我也不确定这是可以接受的行为,或者是否会令人惊讶。
  • @Deduplicator 给定类似const Map&lt;Key, Val&gt; f(); 的东西,f().maybe("key") 将调用const&amp;&amp;-qualified maybeconst&amp;&amp; 不会出现在典型的程序中,因为我们大多数人都意识到从函数返回 const T 是一个坏主意,即使当 T 是类类型时语言确实允许它。
  • @Casey:谢谢,所以即使我忘记了获得它的所有方法,它仍然是病态的。只是delete-ing 它看起来仍然是确保正确的方法,即使使用健全的 API 也不会有意外调用它的危险。

标签: c++ c++11 reference


【解决方案1】:

无论如何,你不可能在好的代码中得到const&amp;&amp;

唯一的方法是

  1. 调用返回const T 的函数。 (无论如何,拥有这样的返回类型是个坏主意。)
  2. 调用返回const T&amp;&amp; 的函数。 (二托。)
  3. 故意转换为const&amp;&amp;。 (无论如何你都不会这样做。)
  4. 来自&amp;&amp; 的隐式转换。 (这不会发生,因为您有一个通过 r 值引用接受的重载。)

因此,如果您想对 API 进行防弹,正确的方法就是 delete-ing 过载:

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

【讨论】:

    【解决方案2】:

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

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

    参考x 将在地图被破坏后悬空(使最后一行未定义行为)。标准库没有采取任何措施来防止这种情况发生。

    我也从未听说有人不小心犯了这种错误。

    你的地图也是一样的情况。

    IMO,根据地图的值类别返回 Maybe&lt;Val&gt;Maybe&lt;Val&amp;&gt; 太令人困惑了。每次在临时对象上调用 .maybe 时请三思。

    【讨论】:

      猜你喜欢
      • 2014-05-25
      • 1970-01-01
      • 2020-03-24
      • 1970-01-01
      • 1970-01-01
      • 2021-01-08
      • 1970-01-01
      • 2011-06-04
      相关资源
      最近更新 更多