You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MinGW64与MSVC函数决议结果差异的C++标准判定及疑问

第一个版本代码编译器差异的标准符合性判断

对应代码:

template <typename T>
int get_num(const T&)
{
    return 42;
}

struct Foo
{
    int i = get_num(*this);
};

int get_num(const Foo&)
{
    return 23;
}

int main()
{
    std::cout << Foo().i << std::endl; // MinGW64 - 42, MSVC - 23
    return 0;
}
  • 结论:MinGW64(GCC)的实现符合C++标准,MSVC的实现不符合标准
  • 原因:
    1. 针对Foo类内默认成员初始化器的get_num(*this)调用,C++标准要求非限定名查找仅考虑调用点之前已经声明的实体。该调用点位于Foo类定义内部,此时全局作用域仅存在模板版本的get_num,非模板重载的声明在Foo类定义结束之后,调用点不可见。
    2. ADL(参数依赖查找)同样不会找到调用点之后才声明的普通非模板函数,因此标准要求下只能选择模板版本的get_num,和MinGW的行为一致。
    3. MSVC的异常行为源于其传统的非标准查找实现:它会将类内初始化器的名字查找延迟到整个类定义完成、甚至翻译单元解析到末尾后执行,因此能看到后续声明的非模板重载,不符合标准规则。
第二个版本代码输出差异的原因

对应代码:

template <typename T>
int get_num(const T& t)
{
    std::cout << "Template version called" << std::endl;
    return get_num(t);
}

struct Foo
{
    int i = get_num(*this);
};

int get_num(const Foo&)
{
    std::cout << "Non-Template version called" << std::endl;
    return 23;
}
  • MinGW的符合标准的行为逻辑:
    1. get_num(*this)调用点仅可见模板版本,因此实例化get_num<Foo>,首先打印模板版本的日志。
    2. 模板内部的get_num(t)是依赖模板参数的调用,会触发两阶段查找的第二阶段:在模板实例化点执行ADL查找,此时非模板版本的get_num(const Foo&)已经声明可见,因此调用非模板版本,打印对应日志并返回23,不会出现无限递归。
  • MSVC的非标准行为逻辑:
    1. 还是因为其延迟查找的实现,在处理get_num(*this)时已经能看到后续声明的非模板重载,非模板重载是比模板更匹配的候选,因此直接调用非模板版本,不会实例化模板,仅打印非模板版本的日志。
  • 该差异确实和MSVC的ADL以及两阶段查找的非标准实现有关,在MSVC中开启/permissive-开关启用标准兼容模式后,也会得到和MinGW一致的输出。

内容的提问来源于stack exchange,提问作者Sergey Kolesnik

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.26 02:15:00