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