为何编译器报错`std::enable_if...::type*=nullptr`,加空格后却正常?
std::enable_if相关参数中*=连写会编译失败? 你的猜测完全正确——编译器在词法分析阶段会把*=当作单个复合赋值运算符,而非将*视为指针类型修饰符、=视为默认值赋值符号,这就是两种写法编译结果不同的核心原因。
背后的词法解析规则
C编译器处理源代码的第一步是词法扫描(Tokenization),它遵循「最长匹配原则」:会尽可能将连续的字符组合成一个合法的语法单元(Token)。*=是C标准定义的复合赋值运算符,属于合法的单个Token,因此当编译器看到type*=时,会直接将*=解析成一个整体,而不是拆分成*和=两个独立Token。
结合你的代码分析
看koo函数的参数声明:
void koo(T val, typename std::enable_if<is_allowed_type<T>::value>::type*=nullptr){}
编译器会把*=识别为复合赋值运算符,导致参数声明结构变成[类型] *= nullptr,这完全不符合函数参数的语法要求——函数参数默认值的正确结构应该是[类型] [参数名(可省略)] = 默认值。这种非法结构直接触发编译器报错,提示expected ')',因为它无法识别参数的正确格式。
而添加空格后的写法:
void hoo(T val, typename std::enable_if<is_allowed_type<T>::value>::type* = nullptr){}
空格打断了*和=的连续性,词法扫描时会将它们拆分为两个独立Token:*作为指针类型修饰符,=作为默认值的赋值符号,此时参数声明就变成了合法的「指向该类型的指针(省略参数名),默认值为nullptr」,符合C++语法规则,因此可以正常编译。
你的测试代码佐证
你写的测试代码直接验证了这一点,和std::enable_if无关,纯粹是词法解析规则导致的:
void foo1(int*=nullptr){} // 编译失败,*=被识别为复合运算符 void foo2(int* =nullptr){} // 编译成功,*和=被拆分为独立Token
补充说明
C中这类「最长匹配」的词法规则还有不少,比如旧标准中>>会被识别为右移运算符而非模板参数闭合(C11后调整了模板场景的处理),但*=这类复合运算符的解析规则从未改变——只要没有空格分隔,就会被识别为单个Token。
内容的提问来源于stack exchange,提问作者John

