以函数指针为key_comp的std::set作为类成员时报“不是类型”错误
问题根因
这个报错是C++类作用域下的最令人头疼的解析(Most Vexing Parse)规则导致的,两处代码的解析逻辑存在本质差异:
- 全局作用域下的
std::set<unsigned, decltype(compare)*> GoodSet(compare);不会触发歧义:编译器在解析到compare时,会先识别出它是已定义的全局函数实体、而非类型名,因此会跳过“函数声明”的解析分支,直接将其识别为GoodSet对象的构造过程,把compare作为构造参数传入,编译顺利通过。 - 但在类的成员声明上下文,只要代码结构符合「返回类型 标识符(内容)」的形态,编译器会强制优先将其解析为成员函数的声明,而非成员对象的构造:
此时编译器会把括号内的所有内容,按“函数形参的类型说明符”来解析。你原本传入的实参compare,在这个解析路径下会被当成“待查找的类型名”处理,但compare是一个函数实体、并非合法类型,因此直接抛出“compare is not a type”的错误。
可行的解决方案
- 方法1:替换圆括号初始化语法为花括号初始化(C++11及以上支持,为最推荐写法)
花括号初始化不会触发“优先解析为函数”的规则,编译器会明确识别为成员对象的构造调用:class BadSet{ public: std::set<unsigned, decltype(compare)*> Set2{compare}; }; - 方法2:使用带等号的拷贝初始化形式
等号的存在会明确告知编译器这是对象初始化,不会和函数声明产生歧义:class BadSet{ public: std::set<unsigned, decltype(compare)*> Set2 = std::set<unsigned, decltype(compare)*>(compare); }; - 方法3:在类构造函数的初始化列表中完成成员初始化
这是C++03时代就支持的全兼容写法,完全不依赖类内就地初始化的语法特性:class BadSet{ private: std::set<unsigned, decltype(compare)*> Set2; public: // 构造函数初始化列表明确指定Set2的构造参数 BadSet() : Set2(compare) {} }; - 方法4:给构造实参加冗余外层括号(经典解析歧义破解技巧)
形参声明语法不允许给单个形参加多余的外层括号,以此破除“优先解析为函数”的规则:class BadSet{ public: std::set<unsigned, decltype(compare)*> Set2((compare)); };
内容的提问来源于stack exchange,提问作者wuyabiao
相关产品推荐
相关产品推荐

