【问题标题】:Different result for function resolution on MinGW64 and MSVCMinGW64 和 MSVC 上的函数解析结果不同
【发布时间】:2021-12-26 04:16:21
【问题描述】:
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;
}

MSVC 选择非模板get_num“重载”。即使模板只是前向声明,它也会成功链接。 MinGW64 将选择默认的模板实现。如果模板没有定义,将无法链接。

谁对谁错?标准是怎么说的?


但是这个版本对两个编译器产生了相同的结果...为什么它不去无限递归?

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;
}

MSVC 输出:

Non-Template version called
23

MinGW64 输出:

Template version called
Non-Template version called
23

我认为在使用 MSVC 时会涉及到 ADL。

【问题讨论】:

  • 您是否在以迂腐、符合标准的模式使用 MSVC 进行编译?
  • @NicolBolas 是的
  • 还有,它是什么版本?
  • @NicolBolas MSVC 19.29.30133.0

标签: c++ visual-c++ c++14 language-lawyer mingw


【解决方案1】:

对于第一个示例,MinGW 正确,MSVC 错误:get_num 调用只能考虑函数模板。

这是一个重载决议的问题,所以我将从子句 [over] 开始。 get_num(*this) 是一个普通的函数调用表达式,所以 [over.call.func] 适用:

在不合格的函数调用中,名称不受-&gt;. 运算符的限定,并且具有更一般的primary-expression 形式。按照函数调用中名称查找的正常规则,在函数调用的上下文中查找名称。通过该查找找到的函数声明构成候选函数集。

“名称查找”在[basic.lookup] 部分讨论。第 2 段:

“在表达式的上下文中查找”的名称在找到表达式的范围内作为非限定名称进行查找。

这里的一个复杂情况是 get_num(*this) 默认成员初始化表达式不会被任何东西使用,直到 Foo 的默认构造函数由其在 main 中的 odr-use 隐式定义。但是查找是根据表达式本身的代码位置确定的,无论它是否用于该隐式定义的过程。

对于第二个代码示例,MinGW 再次正确:模板定义中的明显递归调用实际上调用了非模板函数。

这是依赖函数调用的“两阶段查找”的结果,在[temp.dep.res] 部分中进行了描述。简而言之,由于t 的类型取决于模板参数,所以表达式get_num(t) 中的名称get_num 被认为是从属名称。因此,对于函数模板的每个实例化,它都有两种方法来寻找重载决议的候选者:找到get_num 函数模板的普通直接方法,以及从实例化点开始的另一种查找。特化 get_num&lt;Foo&gt;main() 定义之后具有实例化点,因此重载解析能够从实例化上下文中找到非模板,并且非模板赢得重载解析。

(依赖于参数的查找是与第二点相关的切线问题。ADL 适用于来自实例化上下文的声明,但不适用于来自定义上下文的声明。但这不是任何示例程序中行为的直接原因。)

据我所知,在 C++14 和最新草案之间,这些都没有显着变化。

【讨论】:

  • 如果Foo 的实例化点在get_num(const Foo&amp;) 声明之前,那么第二个示例将无法编译?
  • 格式不正确,不需要诊断,因为翻译单元(或 C++20 模块)的末尾是另一个实例化点,它会给相同的特化赋予不同的含义 ([temp.point]/7) .可能大多数编译器都会编译它,但您必须猜测实际会发生什么。
  • 特化 get_num&lt;Foo&gt;main() 定义之后有实例化点 为什么?
  • @LanguageLawyer [temp.point]/1 当一个函数模板实例化不是由另一个模板实例化引起时,......“这种特化的实例化点紧跟在命名空间范围声明或定义之后,它引用了专业化”。
  • 但是Foo::Foo() 而不是main() 指的是get_num&lt;Foo&gt;
猜你喜欢
  • 1970-01-01
  • 2012-06-14
  • 2016-03-01
  • 1970-01-01
  • 2019-08-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多