类的模板参数名称能否遮蔽该类的成员名称?相关编译器行为差异咨询
类模板参数名称能否遮蔽该类的成员名称?相关编译器行为差异咨询
我遇到了一个涉及C++模板参数与类成员名称冲突的问题,不同编译器的处理结果完全不一样,想和大家讨论一下规范上的正确行为:
先看这段测试代码:
#include <cstdint> class Foo { template <typename Write> void WriteNestedMessage(uint32_t field_number, Write write_message); protected: void Write(); }; template <typename Write> void Foo::WriteNestedMessage(uint32_t field_number, Write write_message) {}
这里的核心点是:类Foo的模板成员函数WriteNestedMessage,其模板参数Write和类自身的保护成员函数Write()重名了,而且我们在类外定义这个模板函数时,同样使用了Write作为模板参数名。
不同编译器的表现
- Clang:毫无压力地通过了编译,没有任何警告或错误。
- GCC:直接报了一堆错误,核心问题是它在处理类外的模板定义时,把
Write识别成了类里的保护成员函数Write(),而不是模板参数:<source>:12:53: error: 'Write' is not a type 12 | void Foo::WriteNestedMessage(uint32_t field_number, Write write_message) {} | ^~~~~ <source>:12:6: error: no declaration matches 'void Foo::WriteNestedMessage(uint32_t, int)' 12 | void Foo::WriteNestedMessage(uint32_t field_number, Write write_message) {} | ^~~ <source>:5:8: note: candidate is: 'template<class Write> void Foo::WriteNestedMessage(uint32_t, Write)' 5 | void WriteNestedMessage(uint32_t field_number, Write write_message); | ^~~~~~~~~~~~~~~~~~ <source>:3:7: note: 'class Foo' defined here 3 | class Foo { | ^~~ <source>:12:53: error: 'void Foo::Write()' is protected within this context 12 | void Foo::WriteNestedMessage(uint32_t field_number, Write write_message) {} | ^~~~~ <source>:8:8: note: declared protected here 8 | void Write(); | ^~~~~ Compiler returned: 1
查看cppreference的相关规则
我翻了cppreference上关于模板参数名称遮蔽的内容,有几条相关规则,但看起来对这个场景的指向有些模糊:
- 对于类模板的函数/类模板成员的类外定义:该成员自己的模板参数会遮蔽类模板的模板参数(反过来则不会)。这条规则明显支持Clang的行为——这里的
Write应该是成员函数自己的模板参数,而非类的成员函数。 - 如果类模板成员的类外定义在类所在命名空间之外,模板参数的名称会遮蔽命名空间成员的名称。这条虽然针对命名空间,但逻辑上也偏向Clang的处理是符合规范的。
- 对于类模板或其成员的类外定义,非依赖基类的成员名称如果和模板参数重名,会遮蔽模板参数。但这条只针对基类成员,我们的场景里
Write是当前类的成员,不是基类的,所以这条规则并不适用GCC的错误判断。
目前这个编译器行为差异已经被提交为相关issue。
内容来源于stack exchange
相关产品推荐
相关产品推荐

