【问题标题】:Can an exception be thrown from the ternary operator?三元运算符可以抛出异常吗?
【发布时间】:2013-12-30 21:49:37
【问题描述】:

有时有一个函数很方便,甚至需要一个语句(返回constexpr时是必要的)。如果需要检查一个条件并且只允许一个语句,则条件运算符是唯一的选择。如果发生错误,最好从条件运算符抛出异常,例如:

template <typename It>
typename std::iterator_traits<It>::reference
access(It it, It end) {
    return it == end? throw std::runtime_error("no element"): *it;
}

但是,当用作 (live example) 时,上述函数无法编译:

std::vector<int> v;
access(v.begin(), v.end());

编译器抱怨试图将非const 引用绑定到临时对象。不过,编译器并没有抱怨 throw-expression 本身。那么问题来了:是否可以从条件运算符中抛出异常,如果可以,上面的代码出了什么问题?

【问题讨论】:

  • 它通过修改返回类型进行编译:ideone.com/pjOkh8
  • @stefan:如果你让函数按值返回那么它会copy,它不再是同一个函数了!
  • @DavidRodríguez-dribes 是真的,但它消除了编译错误并表明至少这个编译器不会在异常情况下阻塞三元运算符。
  • 这可以说是一个缺陷,见CWG 1560CWG 1550
  • 请注意,对于 C++14,三元运算符在 constexpr 中失去了重要性(从 Clang 3.4 开始完全支持,例如在 coliru.stacked-crooked.com 上)。在constexpr 中抛出异常并结合逗号运算符似乎是the idiomatic way to handle errors

标签: c++ c++11 constexpr


【解决方案1】:

条件运算符在 5.16 [expr.cond] 中描述。它的第 2 段包括以下案文:

第二个或第三个操作数(但不是两者)是 throw-expression (15.1);结果是对方的类型,是prvalue。

这表示允许从条件运算符抛出异常。但是,即使另一个分支是左值,它也会变成右值!因此,不可能将左值绑定到条件表达式的结果。除了使用逗号运算符重写条件之外,还可以重写代码以仅从条件运算符的结果中获取左值:

template <typename It>
typename std::iterator_traits<It>::reference
access(It it, It end) {
    return *(it == end? throw std::runtime_error("no element"): it);
}

有点棘手的是,从函数返回 const 引用会编译,但实际上会返回对临时的引用!

【讨论】:

  • 您应该接受这个答案,因为它回答了问题并提供了对可能陷阱的洞察
【解决方案2】:

标准中的措辞大约是 5.16/2:

如果第二个或第三个操作数的类型为 void,则左值到右值 (4.1)、数组到指针 (4.2) 和函数到指针 (4.3) 标准转换将在第二和第三个操作数,并且应满足以下条件之一:

——第二个或第三个操作数(但不是两者)是一个抛出表达式(15.1);结果是对方的类型,是prvalue。

这解释了你得到的行为。 throw 是合法的,但表达式的类型是 pure-rvalue(即使表达式是 lvalue),因此不能绑定非 const 左值引用

【讨论】:

    【解决方案3】:

    可以这样做:

    return it == end? (throw std::runtime_error("no element"),*it): *it;
    

    【讨论】:

    • 这是避免问题的一种方法,但 IMO *(it == end ? throw ... : it) 看起来更整洁。
    • 嘿,这正是我发布此评论时 Dietmar Kühl 在自己的回答中所提出的。 :)
    • @hvd:虽然将取消引用移动到外部看起来更整洁,但它至少在概念上复制了迭代器。使用逗号运算符应该避免复制迭代器的需要。
    • @DietmarKühl 你是对的。但是,复制成本高昂的迭代器并不是我担心的事情,而且考虑到您按值获取迭代器参数这一事实,您似乎也不必担心。所以在这种情况下,可读性对我来说具有更高的优先级,但你和其他人可以合理地得出不同的结论。
    • @hvd 我认为您/Dietmar 解决方案的真正优势在于您不必重复表达。我的解决方案的优点是甚至可以用于更复杂的表达式(并且可以缩短逗号运算符之后的第一个表达式)。也许应该根据具体情况决定哪种风格更合适。
    猜你喜欢
    • 2016-01-08
    • 2013-10-28
    • 2016-03-04
    • 1970-01-01
    • 2016-11-15
    • 2020-05-10
    • 2011-02-09
    • 2012-03-11
    • 2012-02-09
    相关资源
    最近更新 更多