Clang LLVM C ++`std :: optional <std :: optional <int >>`令人惊讶的比较行为

时间:2018-10-12 11:47:06

标签: c++ c++17 clang++

我有一个函数backend,它可以加载给定页面或所有页面(如果是load(std::optional<int> page))。因为加载是一项昂贵的操作,所以我缓存了最后加载的页面及其内容。为此,我正在使用类型为page.empty()的成员变量,其值应告诉我当前是缓存单个页面,所有页面还是根本没有页面。

LLVM的libc ++实现(与clang std::optional<std::optional<int>>一起提供)在比较std :: optional实例时具有令人惊讶的行为,这与Apple LLVM version 10.0.0 (clang-1000.11.45.2)所做的不同(已通过1.67测试):

boost::optional

这是正确的行为,这是libc ++实现中的错误吗?

3 个答案:

答案 0 :(得分:4)

行为正确:

https://en.cppreference.com/w/cpp/utility/optional/operator_cmp

template< class T, class U > 
constexpr bool operator!=( const optional<T>& lhs, const optional<U>& rhs ); (2)
     

对可选对象执行比较操作。

     

1-6)比较两个可选对象lhs和rhs。包含的值   仅当两个lh都被比较时(使用T的相应运算符)   和rhs包含值。否则,

     
      当且仅当lhs和rhs都符合   不包含值。   

std::optional<T>的默认构造函数构造了一个不包含值的对象,因此std::optional<int>>()std::optional<std::optional<int>>()都不包含值,因此它们相等。

答案 1 :(得分:2)

作为旁注(但由于我们一直在这里),我个人不太喜欢您的设计。作为您的代码的使用者,我希望load(<empty optional>)可以加载...没有页面。这就是可选的含义。

一种解决方案是具有两种不同的功能:

void load(int);
void load_all();

如果这对于您来说太激进了,那么您可以通过以下几种方法来重载:

struct load_all{};

void load(int);
void load(load_all);

我真的很想明确我的要求和得到的东西。这就是为什么我强烈喜欢上面的方法而不是下一个解决方案的原因:

void load(int);
void load();

为了完整起见,尽管我实际上并不推荐这样做(因为与std::variant配合使用的工具很繁琐,否则可以很好地表达其意图):

struct load_all{};

void load(std::variant<int, load_all>);

答案 2 :(得分:1)

这实际上是很有趣的……如果模棱两可,这种行为是友善的。比较应该应该做什么没有明确的答案。根据实现选择,标准库和Boost会做不同的事情。没有人是错的。

std::optional具有混合的比较。这意味着有些运算符将optional<T>optional<U>U进行比较。在此模型中,最佳匹配是将双方都视为可选-因此,由于双方均未接合,因此它们的比较是平等的。

但是boost::optional仅具有相同的类型比较。意味着optional<T>仅可与optional<T>T相提并论。在此模型中,optional<int>被解释为optional<optional<int>> value 。因此,我们有一个脱节的可选值和一个值-因此它们比较不相等。

我不确定此比较是否具有明确的含义,因此最好避免使用它。