为何""s.size()合法而5ms.count()不合法?语法规则背后的原理是什么?
先看你给出的代码示例:
#include <chrono> #include <string> using namespace std::literals; int main() { ""s.size(); // ok (""s).size(); // ok (5ms).count(); // ok 5ms.count(); // error }
报错信息是:error: unable to find numeric literal operator 'operator""ms.count'
咱们来拆解这个问题的核心,其实就是C++编译器对**用户定义字面量(UDL)**的解析规则在起作用:
先看合法的情况:
""s.size()""s是C++标准库提供的字符串用户定义字面量,编译器会把""和后面的s绑定成一个整体,解析为一个std::string对象。之后的.size()就是普通的成员函数调用——因为.是成员访问运算符,它的解析是在UDL绑定完成之后,所以整个表达式等价于(std::string("")).size(),完全合法。再看报错的情况:
5ms.count()
这里的问题出在编译器的解析顺序上。C语法规定,用户定义字面量的格式是字面量""标识符,其中的**标识符必须是一个合法的C标识符**(只能由字母、数字、下划线组成,不能包含.这类符号)。当编译器看到5ms.count()时,它会尝试把ms.count当作一个完整的UDL标识符去匹配,而不是先把5ms解析为std::chrono::milliseconds对象再调用count()成员函数。因为不存在名为ms.count的UDL运算符,所以就抛出了找不到operator""ms.count的错误。
那为什么加个括号(5ms).count()就合法了?因为括号改变了编译器的解析优先级:编译器会先解析括号内的5ms,把它识别为duration对象,之后再通过.调用count()成员函数,这就和(""s).size()的逻辑一致了。
背后的设计原理其实是为了语法的确定性:如果允许UDL标识符中包含.,编译器会无法区分你到底是想调用UDL还是成员函数,会产生歧义。比如如果有个UDL叫ms.count,那5ms.count()到底是调用这个UDL,还是先解析5ms再调用count()?为了避免这种歧义,C++标准干脆规定UDL的标识符必须是合法的、不含特殊符号的标识符,强制编译器优先按照UDL的完整格式去匹配,而不是拆分标识符和成员访问。
内容来源于stack exchange

