模板参数能否引用基类成员函数?多编译器兼容性问题解析
GCC与MSVC模板基类成员调用的编译差异解析
问题代码
template<class TParams> class Selector : public TParams::DataSource1, public TParams::DataSource2 { using DataSource1 = typename TParams::DataSource1; using DataSource2 = typename TParams::DataSource2; public: template<class Fn> static void ForEachDataSource(Fn && fn) { fn.template operator()<DataSource1>(); fn.template operator()<DataSource2>(); } void Select(int i) { ForEachDataSource([&, this]<typename T>() { this->T::Query(i); }); } };
问题场景与编译错误
Selector类继承自TParams中指定的两个基类DataSource1和DataSource2,二者均提供Query成员函数。为避免重复代码,将遍历调用基类Query的逻辑抽离到ForEachDataSource静态函数中,但这段代码在不同编译器中表现不一致:
- GCC编译报错:
error: 'T' is not a class or namespace - 若省略
this->,MSVC报错:error C2352: 'DataSourceF::Query': a call of a non-static member function requires an object
另外,显式将this转换为T*类型可在所有编译器通过,但需要适配const与非const限定的函数;而直接写出this->DataSource1::Query(i)这类具体基类调用的代码,在所有编译器中均正常编译。
疑问
C++标准是否禁止使用模板参数指代基类并调用其成员函数?哪种编译器的行为符合标准?
解答
- 标准规定:C++标准并不禁止使用模板参数指代基类并调用其成员函数,但对调用语法有明确要求。
- 编译器行为分析:
- GCC的行为符合标准:
this->T::Query(i)的写法存在语法问题,this->后的T::属于限定名,但T是lambda的模板参数,编译器在模板实例化前无法确定T是Selector<TParams>的基类,因此无法将T::解析为this指向对象的基类限定,报错是合理的。 - MSVC的行为属于非标准扩展:它将
this->T::隐式处理为static_cast<T*>(this)->的形式,这并不符合C++标准的规定。
- GCC的行为符合标准:
- 正确写法:
显式将this转换为T类型指针是符合标准的通用写法,可适配不同限定符:
也可以使用成员函数的等价调用形式:// 非const版本 static_cast<T*>(this)->Query(i); // const版本 static_cast<const T*>(this)->Query(i);T::Query(*this, i);
内容的提问来源于stack exchange,提问作者Osyotr
相关产品推荐
相关产品推荐

