g++ 编译器模板解析近似缺陷的限制(C++标准议论)

最近想把我的C++模板库移植到linux,用g++编译,发现了大量编译错误,逐个仔细审查外,我更多地倾向于认为是linux g++编译器对复杂模板元编程的解析能力很弱,而不是代码问题。

1. vs编译器支持的模板库

这套模板库(https://github.com/shenxiaolong-code/MiniMPL),从vs2008的C++03, 直到验证过的支持C++20的vs2019,(估计以后版本的vs也可以,没测试过),每个vs版本都支持得很完美,但在g++上,各种应该支持的模板解析都编译报错。

2. g++对模板的语法要求

并且g++对模板的语法要求非常生硬,比如:

  • 对于子类型,一律要求使用typename来指示,
  • 对于模板成员函数,要求使用来指示,比如obj.template run<...>(...),
  • 对于内嵌的模板类,要求使用::template来指示,比如T::template apply<...>;

而vs就宽松得多,在不出现多重定义的情况下,会自动探测到应该使用什么,并不需要用户显式指定,除非碰到有岐义的地方。

3. 模板特化的问题

最近碰到一个模板特化的问题,我的模板特化的终结类值是依赖于外层模板类的输入参数的,这就导致我的模板特化终结器必须定义在类内。
MS编译器没有任何问题,各种MS编译器就完美支持(MSBuild , visual studio), 而 g++ 就报告说不支持类内特化。

error: explicit specialization in non-namespace scope ‘xxxxxxx’

4. perplexity AI的结论

和 perplexity AI 反复讨论后得出的结论就是: g++ 编译器没法满足这种真实的需求。
perplexity 的总结:

1. 现状总结
你的代码逻辑是合理的,且符合C++模板编程的常见模式。
由于GCC不支持类内特化,而你又需要这种特化的功能,这确实是一个限制。
可能的解决方案
虽然GCC的限制可能看起来像是一个bug,但实际上这是编译器对C++标准的实现方式。我们可以尝试一些替代的设计模式,尽管这些可能不完全符合你的初衷,但可以实现类似的功能。

2 根据你提供的代码和错误信息,以及搜索结果中的讨论,g++确实不支持在类内部进行显式特化。这是一个g++的限制,而不是你代码中的问题。
所以你提供的两种方式,都属于在类内部进行特化,g++都会报错。这是由于g++的实现限制造成的。

即: perplexity 确认我的代码是合理的,并且符合C++模板编程. g++编译报错的原因是因为g++的限制。


5. g++编译器无法支持的模板代码

源文件链接: enumTrait_detail.hpp#L23

template<typename T>                        struct EnumRangeSizeImpl;
template<typename F,F F_min,F F_max>        struct EnumRangeSizeImpl< CEnumRange<F,F_min,F_max> >
{
    typedef CEnumRange<F, F_min, F_max>     TEnumRange;

    struct detail
    {
        template<typename TEnumValue>   struct CountHelper: public Int2Type<1+CountHelper<typename TEnumValue::template Next<true>::type >::value >{};
        template<>                      struct CountHelper<typename TEnumRange::Max_T >   : public Int2Type<1> {};
    };

    enum {value=detail::CountHelper<typename TEnumRange::Min_T >::value};
};

简单解释:
这是一个计算枚举类型枚举值数量的模板函数(计算某枚举类型有几个枚举值),防止枚举时越界。

由于 CountHelper 的终结器依赖于EnumRangeSizeImpl类的输入模板参数 TEnumRange,所以其只能在类EnumRangeSizeImpl的内部进行特化。 然后编译 CountHelper<typename TEnumRange::Max_T >  时,g++就编译出报错了:

error: explicit specialization in non-namespace scope ‘struct MiniMPL::EnumRangeSizeImpl<MiniMPL::CEnumRange<F, F_min, F_max> >’

6. 个人浅见(C++标准议论)

这是模板编程的真实需求,并不是臆造的。

如果一个特化终结器依赖于一个类型,那么它必须在一个类内,只有类进行实例化编译时,这个类的输入参数类型才是确定的,从而这个终结器的值才是确定的,任何类外的终结器都是没法实现的(无法提供一个明确的值)

把类的特化限制在命名空间内,而强制不能在类内特化,这是C++03的标准规定,可能是初期是由于编译器厂商实现上的限制,但是我发现随后的C++标准到现在一直都没有更新

C++03标准 14.7.3节提到:

An explicit specialization shall be declared in the namespace of which the template is a member, or, for member templates, in the namespace of which the enclosing class or enclosing class template is a member.

但是MS编译器并没有遵循这个要求,而是支持类内特化
Stack Overflow上的讨论:

VC++ is non-compliant in this case - explicit specializations have to be at namespace scope. C++03, §14.7.3/2:
An explicit specialization shall be declared in the namespace of which the template is a member, or, for member templates, in the namespace of which the enclosing class or enclosing class template is a member.

希望C++标准能取消这个限制,根据商业编译器(如MS编译器)的实践经验来更新标准,同时g++编译器也应该增强它的模板解析能力。

  • 14
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值