GCC 上有不明确的构造函数重载,但 Clang 上没有

apo*_*opa 19 c++ gcc templates clang language-lawyer

假设我们有以下简单的代码:

#include <iostream>
#include <type_traits>

struct S {
    template <typename T> explicit S(T) noexcept requires std::is_signed<T>::value {
        std::cout << "T\n";
    }
    template<typename T> explicit S(const T&) noexcept {
        std::cout << "const T&\n";
    }
};

int main() {
    S s(4);
}
Run Code Online (Sandbox Code Playgroud)

此代码使用 Clang 编译并打印“T”,但使用 GCC 时出现以下错误:

error: call of overloaded 'S(int)' is ambiguous
Run Code Online (Sandbox Code Playgroud)

我的问题是:GCC 还是 Clang 哪个编译器有 bug?

Bri*_*ian 28

海湾合作委员会是正确的。这是模棱两可的。

首先,我们必须看看隐式转换序列。在这两种情况下,都涉及身份转换序列:inttointintto const int&(由于[over.ics.ref]/1 ,后者被认为是身份转换序列)。

其次,我们看一下有关标准转换序列的决胜规则,[over.ics.ref]/3.2。这些决胜局均不适用于这种情况。这意味着两个隐式转换序列都不比另一个更好。

接下来我们必须进入全球决胜局。即使当一个重载的所有隐式转换序列既不比另一个重载的相应隐式转换序列更好也不更差时,这些也可以允许一个重载被认为比另一个重载更好。全局决胜局在[over.match.best.general]/2中定义。根据第五个要点(其他任何一个都不可能适用于这种情况),如果两个重载都是模板专业化,但一个模板比另一个模板更专业,则其中一个重载可能比另一个更好。

为了确定是否是这种情况,我们参考[temp.func.order],它参考[temp.deduct.partial]。我们处于函数调用的上下文中,因此根据(3.1),“使用的类型是函数调用具有参数的函数参数类型”。然后,第 5 段去除参考文献,第 7 段去除顶级简历限定符。其结果是推论在两个方向上都成功。(也就是说,即使不是每个T都是 a const U&,推导无论如何都会在这个方向上成功,因为在实际推导发生之前const U&被替换为。)U

回到 [temp.func.order],由于推导在两个方向上都成功,因此第 2 段中提到的最终决胜因素是一个模板是否比另一个模板受到更多约束。为此,我们向下滚动到第 6 段。适用的要点是 (6.2.2),根据该要点:

否则,如果template- parameter-list 对应的 template- parameter不等价 ([temp.over.link]) 或者如果两个模板之间位置对应的函数参数不是同一类型,则两个模板比另一个更专业。

请注意,在这种情况下,引用和 cv 限定符的剥离不适用,因为这仅作为推导的一部分完成,并且我们不再进行推导,因此位置对应的函数参数类型是Tconst T&,其中不一样。因此,这两个模板都不比另一个更专业,这意味着最终的决胜局未能优先选择其中一个过载。

  • 感谢您的解释和参考,我现在明白为什么 GCC 正确实施了该标准。 (4认同)