C++函数调用中unqualified-id包含不同unqualified-name的示例疑问及ADL规则解析请求
一、unqualified-id包含不同unqualified-name的示例
首先得明确C++语法里unqualified-id的构成:它不包含作用域限定符(比如::),可以是普通标识符、运算符函数名、转换函数名,或者带模板参数的模板id等。其中最直观的unqualified-id与内部unqualified-name不同的场景,就是模板实例化的调用:
比如下面的代码:
template<typename T> void print(T val) { std::cout << val << std::endl; } int main() { print<int>(100); // 这里的`print<int>`就是一个unqualified-id }
在这个例子里,print<int>是完整的unqualified-id,而它内部包含的unqualified-name是print——也就是模板本身的核心标识符。两者明显不同:unqualified-id是带模板参数的完整调用标识,而unqualified-name是其中的基础名称。
另外还有运算符函数的场景,比如operator+作为unqualified-id时,它的unqualified-name就是operator+,虽然文本一致,但从语法结构上,operator-function-id属于unqualified-id的子类,也算unqualified-id包含unqualified-name的情况。不过模板id的例子更能体现“两者不同”的特点。
二、解析示例中(f)(s)的结构与错误原因
先看你给出的代码片段:
namespace N { struct S { }; void f(S); } void g() { N::S s; f(s); // OK: calls N::f (f)(s); // error: N::f not considered; parentheses prevent argument-dependent lookup }
1. 结构拆解
C++的函数调用语法是postfix-expression ( expression-list_opt ),这里的核心是postfix-expression的类型:
- 对于
f(s):f是一个unqualified-id,符合标准中触发ADL的前提条件。 - 对于
(f)(s):(f)是一个括号表达式(parenthesized-expression),属于primary-expression范畴,而非unqualified-id。此时函数调用的postfix-expression是这个括号表达式,不再是直接的unqualified-id。
2. 错误原因
根据你提到的C++标准草案6.5.4节规则:只有当函数调用的后缀表达式是unqualified-id,且非限定查找找不到类成员、块作用域函数、非函数/函数模板的声明时,才会触发实参依赖查找(ADL)。
在f(s)的场景中:
- 非限定查找
f时,当前作用域(g函数内部、全局作用域)都没有f的声明,满足ADL触发条件; - ADL会自动去实参
s所在的命名空间N里查找,找到N::f(S),因此调用成功。
而在(f)(s)的场景中:
- 后缀表达式是
(f),不是unqualified-id,因此完全不会触发ADL; - 编译器只会执行非限定查找
f,但当前作用域没有f的声明,所以编译报错,提示找不到f的定义。
简单来说,括号把f包装成了普通表达式,破坏了ADL触发的核心前提——后缀表达式必须是unqualified-id。
内容的提问来源于stack exchange,提问作者user15071942

