首页 > 解决方案 > 标准是否严格定义了该程序应如何编译?

问题描述

以下程序是为了滥用 gcc/msvc 中两阶段查找的一些特性而构建的。它与 gcc/msvc 和 clang 都可以很好地编译,但会导致函数的返回值不同g

struct A;
struct C {};

struct D {
    D (const A &);
};

struct B {
    void f (const C&,int){x=0;};
    void f (const D&,char){x=1;};
    int x;
};

template<typename T>
int f(const A &y)
{
    B x;
    x.f(y,0);   // Line 18
    return x.x;
}

struct A
{
    operator C () const;
};

int g (const A&x)
{
    return f<int>(x);
}

https://gcc.godbolt.org/z/pqAVsU

GCC 和 MSVC 都调用A::operator C并返回 0,而 Clang 调用D(const A &)并返回 1。

根据标准,clang是正确的并且在struct A尚未声明时应该解决第18行的调用还是未指定行为的情况?

标签: c++language-lawyername-lookup

解决方案


[ temp.dep.candidate ] 说,这可能与编译器故障有关(注释和强调我的):

对于后缀表达式[即指定函数的表达式,此处f] 是从属名称的函数调用,使用通常的查找规则([basic.lookup.unqual]、[basic.lookup. argdep]) 除了:

  • [2PL/ADL 行为描述]

如果调用格式错误或找到更好的匹配,则在关联命名空间中的查找考虑所有翻译单元中这些命名空间中引入的具有外部链接的所有函数声明,而不仅仅是考虑在模板定义和模板中找到的那些声明实例化上下文,则程序具有未定义的行为

但是,x.f(y,0);依赖T任何一点,因此上面的可怕段落不适用。我们是在非依赖查找的情况下,由 [ temp.nondep ] 覆盖(注意我的):

模板定义中使用的非依赖名称是使用通常的名称查找找到的,并在使用它们时绑定。[示例如下]

整个表达式应该在::f' 定义的上下文中解析,此时A不完整并且其转换运算符可见。Clang 是对的,而 GCC 和 MSVC 都是错误的。


推荐阅读