【发布时间】: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<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& 版本,这很糟糕,因为这会返回一个引用。
我考虑将&& 版本设为const&& 版本以避免重复;但是,那么我需要复制,而不是移动内部值。而且我对const&& 的语义还不够了解,无法知道const_cast 在内部是否可以接受,或者当我对const&& 进行变异时,这是否会导致疯狂和歇斯底里。如果不需要,我宁愿不必编写此函数的两个副本。
在这种情况下,通常的最佳做法是什么?我需要编写所有 4 个函数吗?或者我可以用 3 理智地逃脱吗?
有没有更好的方法来避免这个悬空引用问题?这只是一个问题,因为auto 通常会剥离引用,所以你不会意外地引用临时的,但是因为Maybe 的类型已经是一个普通值,它只是包装了一个引用类型,它有可能射中自己的脚。只是说“那么在那种情况下不要使用auto”很诱人,但仍然很容易不小心搞砸,我宁愿很难做错事。
【问题讨论】:
-
这个问题可能有太多的上下文,但我试图避免出现 XY 问题场景。
-
你有什么理由不只是
deleteconst&&-variant?我的意思是,这似乎是病态的。另外,我不确定如何在不故意转换为const&&或从其他品种之一隐式转换的情况下获得const&&... -
我并没有真正想到
delete它,但我也不确定这是可以接受的行为,或者是否会令人惊讶。 -
@Deduplicator 给定类似
const Map<Key, Val> f();的东西,f().maybe("key")将调用const&&-qualifiedmaybe。const&&不会出现在典型的程序中,因为我们大多数人都意识到从函数返回const T是一个坏主意,即使当T是类类型时语言确实允许它。 -
@Casey:谢谢,所以即使我忘记了获得它的所有方法,它仍然是病态的。只是
delete-ing 它看起来仍然是确保正确的方法,即使使用健全的 API 也不会有意外调用它的危险。