可以从三元运算符抛出异常吗?

时间:2013-12-30 21:49:37

标签: c++ c++11 constexpr

有时候只有一个语句(返回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 - 表达式本身。所以问题是:可以从条件运算符抛出异常,如果是这样,上面的代码出了什么问题?

3 个答案:

答案 0 :(得分:16)

条件运算符在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引用会编译但实际上返回对临时引用的引用!

答案 1 :(得分:13)

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

  

如果第二个或第三个操作数的类型为void,则执行左值到右值(4.1),数组到指针(4.2)和函数到指针(4.3)的标准转换。第二和第三个操作数,以下之一应保持:

     

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

这解释了您获得的行为。抛出是合法的,但表达式的类型是 pure-rvalue (即使表达式是 lvalue ),因此你不能绑定非const < EM>左值参考

答案 2 :(得分:12)

可以这样做:

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