std :: optional的模棱两可的运算符重载?

Posted

技术标签:

【中文标题】std :: optional的模棱两可的运算符重载?【英文标题】:Ambiguous operator overload for std::optional? 【发布时间】:2017-03-21 13:06:02 【问题描述】:

考虑this 代码。

struct Test : public std::optional<int>   ;

Test o1, o2;
o1 == o2;

对于最后一行gcc-7.0.1 抱怨error: ambiguous overload 指向两个重载,用于比较一个可选值和一个值。但是它不应该只是选择带有两个可选的重载并成功编译代码吗?


编辑 值比较的重载不应该有“守卫”不允许它们实例化从 std::optional 继承的类型吗?

【问题讨论】:

一个更好的问题是你为什么要从optional继承? @Nicol Bolas 好吧,为了给它增加一些额外的语义负载 ։) 建立一个私有成员可能是另一种选择,但这是一个更糟糕的选择,因为我需要 std::optional 的所有功能提供另外我正在添加更多功能。不想重新定义reset()、emplace()等 【参考方案1】:

将为这个表达式找到三个重载:

// #1, with T=int, U=int
template <class T, class U> 
constexpr bool operator==(optional<T> const&, optional<U> const& );

// #2, with T=int, U=Test
template <class T, class U> 
constexpr bool operator==(optional<T> const&, U const& );

// #3, with T=int, U=Test
template <class T, class U> 
constexpr bool operator==(U const&, optional<T> const& );

#2#3 具有比 #1 更好的转换序列,因为其中一个参数(U const&amp;)是 Test 的精确匹配,而不是派生到基础的转换。

但是,虽然我们可以更喜欢 #2#3 而不是 #1,但没有理由更喜欢 #2#3 中的一个 - 每个参数在一个参数中都有更好的转换顺序,而更差第二个转换顺序。

因此,它是模棱两可的。请注意#2#3 都是格式正确的运算符,因为您确实可以将intTest 进行比较。为了消除它们的歧义,您必须另外添加一个U 不继承自optional&lt;T&gt; 的约束,这是...一个非常具体的约束。


您可以通过将一个或另一个 Test 转换为 optional&lt;int&gt; 或简单地提供一个 optional==(Test, Test) 来解决此问题。

【讨论】:

最后我添加了optional==(Test, Test),但从库的角度来看,是否应该更喜欢从 std::optional 派生的类而不是这些重载的值? 我认为问题(见问题中的编辑)不是为什么这些重载是模棱两可的,而是为什么委员会没有选择一组不同的重载(与某些 enable_if 相同?)不会模棱两可。 @MarcGlisse 一组额外的重载特别用于Uoptional&lt;T&gt;继承的情况? 我没有说这是个好主意,但这就是我对问题的解释。 @Barry 是的,对于U 继承自optional&lt;T&gt; 或可转换为optional&lt;T&gt; 的情况,有一组额外的重载。

以上是关于std :: optional的模棱两可的运算符重载?的主要内容,如果未能解决你的问题,请参考以下文章

std::optional<T> 的开销?

在哪些情况下 std::optional operator == 会导致未定义的行为?

为啥 std::optional 不允许“移动构造和仅复制分配”类型的移动分配?

使用 + (unary plus) 为 lambda 解决函数指针和 std::function 上的模棱两可的重载

由于使用声明,字节和模棱两可的符号?

Swift“运算符'>'的模棱两可使用”