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

Die*_*ühl 23 c++ constexpr c++11

有时候只需要一个语句(返回时就必须这样)是方便的,甚至是必要的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;
}
Run Code Online (Sandbox Code Playgroud)

但是,当用作例如(实例)时,上述函数不会编译:

std::vector<int> v;
access(v.begin(), v.end());
Run Code Online (Sandbox Code Playgroud)

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

Die*_*ühl 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);
}
Run Code Online (Sandbox Code Playgroud)

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


Dav*_*eas 13

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

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

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

这解释了你的行为.抛出是合法的,但表达式的类型是纯rvalue(即使表达式是左值),因此你不能绑定非const 左值引用


Dan*_*rey 12

它可以这样做:

return it == end? (throw std::runtime_error("no element"),*it): *it;
Run Code Online (Sandbox Code Playgroud)