为什么std::optional::value_or没有针对默认构造函数类型的专门化?

3
std::optional<Class> a;
a.value_or( Class() ).GetMember();

为什么我们不能像这样做:

a.value_or().GetMember();

标准库能否在默认构造时专门定制 value_or 方法?


2
好问题。略带玩笑的回答是,它不存在,因为没有人说服C++标准委员会它的必要性。 - Bathsheba
3
如果有必要,这个函数最好被称为“value_or_default()”。在“or”后面没有任何东西(甚至不包括参数)是有些奇怪的吧?它肯定读起来不太流畅。 - Max Langhof
3
你可以始终使用 a.value_or({}).GetMember(); - Alexander Kondratskiy
2
让我烦恼的是,即使没有使用,参数也会为value_or构建。对于廉价默认对象来说,这只是小小的烦恼;但对于昂贵的默认对象来说,这就是一个大问题——如果是这种情况,使用value_or可能不是正确的选择。 - Eljay
2
很抱歉,@AlexanderKondratskiy,那样做不行。编译器无法推断参数的类型。 - Valeriy Savchenko
显示剩余4条评论
1个回答

2

很不幸,这个value_or没有这个选项,我同意Max Langhof的评论,应该是一个单独的函数value_or_default

然而,正如Eljay在这条评论中指出的那样,问题中的代码可能会对具有昂贵默认构造函数的类造成问题。在optional不携带值的情况下,我们不希望调用默认构造函数。

我为这种特定情况设计了一个小的解决方案:

struct default_constructor_t {
  explicit default_constructor_t() = default;
};

inline constexpr default_constructor_t default_constructor{};

class Class {
...
  Class(default_constructor_t) : Class{} {}
...
};

int GetMember(std::optional<Class> Object) {
  return Object.value_or(default_constructor).GetMember();
}

在这个解决方案中,您需要为您的类添加一个附加的隐式构造函数。这个解决方案也是懒惰的。它只会在optional没有值时调用默认构造函数。更新:经过一番思考,我相信我想出了一个更通用的解决方案,即使对于开发者无法修改的类型也可以使用。而且即使有这种能力,代码越少越好,对吧?
struct DefaultConstructed {
  template <class T> operator T() const {
    return T();
  }
};

constexpr DefaultConstructed Default;

class Class {
...
};

int GetMember(std::optional<Class> Object) {
  return Object.value_or(Default).GetMember();
}

这个解决方案使用转换运算符而不是隐式构造函数。特殊对象Default可以通过调用该类型的默认构造函数被转换(可以这么说)为任何类型。它还保留了原始解决方案的惰性属性。

你的解决方案比原始代码还要_长_!! 即使只考虑调用点!这样做的目的是什么? - Lightness Races in Orbit
除非可选项不携带值,否则它不会构造实际对象。在默认构造不免费的情况下,不满意是完全有道理的。此外,第二个解决方案只添加到一个头文件中,并且可以与任何类型一起使用。我认为写Default而不是TypeName()并没有那么长。 - Valeriy Savchenko
啊,好的,我明白了。我没有想到有人会关注那个问题,因为问题并没有真正说明OP想要什么,所以我认为这是关于代码长度的;)也许最好在回答开头扩展一下,以澄清你正在解决哪个问题。但是,这确实是解决那个问题的好方法。 - Lightness Races in Orbit
谢谢指出,我已经编辑了答案,包括那一部分信息。 - Valeriy Savchenko

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接