MSVC与Clang函数模板调用差异及免template调用方案问询
问题解答
1. MSVC与Clang编译结果差异的原因
这本质是依赖名称的模板成员调用规则的实现差异:
- 当
TPolicy是模板参数时,TPolicy::Create属于依赖于模板参数的名称(dependent name)。根据C++标准,调用这类名称下的模板成员函数时,必须显式使用template关键字消除语法歧义——编译器无法提前确定Create是普通成员还是模板成员。 - Clang严格遵循标准规则,因此无论调用时是否带实参,都要求添加
template关键字。 - MSVC实现了非标准扩展:当调用
Create时提供函数实参(比如(5)),编译器可通过实参推导反向确认Create是模板成员,因此允许省略template;但无实参调用时没有推导依据,只能遵循标准要求添加template。 - 直接调用
basic_policy::Create时,basic_policy是具体类型而非模板参数,Create不属于依赖名称,编译器可直接识别它是模板成员,因此不需要template关键字。
2. 避免在Create前加template的方案
想要直接写TPolicy::Create<type>(...),可以通过封装函数统一处理模板关键字,把重复的template调用隐藏在封装层:
方案示例:封装自由函数
#include <concepts> #include <utility> template <typename PolicyType, typename T, typename... Ts> concept concept_policy = requires(Ts&&... args) { { PolicyType::template Create<T>(args...) } -> std::same_as<T*>; }; struct basic_policy { template <typename T, typename... Ts> static T* Create(Ts&&... args) { return new T { std::forward<Ts>(args)... }; } }; // 封装策略调用的自由函数 template<concept_policy<int> TPolicy, typename T, typename... Ts> T* policy_create(Ts&&... args) { return TPolicy::template Create<T>(std::forward<Ts>(args)...); } struct type_a { int m_val; }; template <concept_policy<int> TPolicy = basic_policy> static void DoSomething() { // 直接调用封装函数,无需写template关键字 type_a* type1 = policy_create<TPolicy, type_a>(5); type_a* type2 = policy_create<TPolicy, type_a>(); } int main() { DoSomething(); { basic_policy policy; type_a* type1 = basic_policy::Create<type_a>(5); type_a* type2 = basic_policy::Create<type_a>(); } return 0; }
其他调整思路
如果可以修改策略类的设计,也可以:
- 将
Create的模板参数T转换为函数参数(传递T的类型标签),让编译器通过函数参数推导识别模板,避免显式指定模板参数:// 定义类型标签 template<typename T> struct type_tag {}; // 修改后的策略类 struct basic_policy { template <typename T, typename... Ts> static T* Create(type_tag<T>, Ts&&... args) { return new T { std::forward<Ts>(args)... }; } }; // 调用方式(无需template关键字) type_a* type1 = TPolicy::Create(type_tag<type_a>{}, 5);
内容的提问来源于stack exchange,提问作者kbz
相关产品推荐
相关产品推荐

