C++ 中具有相同参数类型和 require 子句的静态和非静态成员函数模板

问题描述 投票:0回答:3

参数类型相同的静态和非静态成员函数不能重载。但是,如果成员函数是模板并且其中之一具有

requires
子句,那么所有编译器都允许它。但是当调用成员函数的both时就会出现问题:

struct A {
    static int f(auto) { return 1; }
    int f(auto) requires true { return 2; }
};

int main() {
    [[maybe_unused]] int (A::*y)(int) = &A::f; // ok everywhere (if no below line)
    [[maybe_unused]] int (*x)(int) = &A::f; //ok in GCC and Clang (if no above line)
}

如果

main()
中只剩下一行(任意),则 GCC 和 Clang 接受该程序。但是当
main()
中的两行都存在时,Clang 会打印

error: definition with same mangled name '_ZN1A1fIiEEiT_' as another definition

GCC 报告内部编译器错误。演示:https://gcc.godbolt.org/z/4c1z7fWvx

所有编译器在接受带有重载静态和非静态成员函数的

struct A
定义时都是错误的吗?或者他们只是在调用两个函数时有类似的错误。

c++ static language-lawyer overloading
3个回答
1
投票

这真是太乱了!在浏览标准时,我在 [over.over]:

中发现了这个简单得多的示例
struct X {
  int f(int);
  static int f(long);
};

int (X::*p1)(int)  = &X::f;     // OK
int    (*p2)(int)  = &X::f;     // error: mismatch
int    (*p3)(long) = &X::f;     // OK

所以我首先尝试了两个有效的行,但即使它们也被所有三个编译器拒绝(彼此独立)。这是一个问题。

进一步看,关于函数模板的地址,[temp.deduct.funcaddr]说:

模板参数可以从获取重载集的地址时指定的类型推导出来。 如果有目标,则将函数模板的函数类型和目标类型作为P和A的类型,并按照[temp.deduct.type]中的描述进行推导。 否则,用类型 P 和 A 的空集进行推导。

此类目标在 [over.over] 中进行了描述,在您的示例中属于第一种类型:

(1.1) 正在初始化的对象或引用([dcl.init]、[dcl.init.ref]、[dcl.init.list]),

即使在考虑任何约束之前,在我看来,编译器应该能够在这两种情况下识别唯一的候选者,就像在更简单的示例中一样,因为目标具有不同的签名。


0
投票

根据C++20标准的最新草案,class.static.mfct#2:

静态成员函数和非静态成员函数不能有相同的名称和相同的参数类型([over.load])。

这里也不例外,存在用于区分成员函数的

requires
子句,只是名称相同和参数类型相同。所以
struct A
的定义在 C++20 中是错误的。

同一项目在 C++23 的初稿中被重新表述,class.static.mfct#2

静态和非静态成员函数不能具有相同的名称、参数类型列表和

尾随的requires-clause([over.load])。

照此,

A

的定义已经没问题了。看起来 GCC、Clang 和 MSVC 即使在 C++20 模式下也都遵循这个声明。我们观察到的错误是由于两个函数的名称相同而发生的(很难修复以保留当前的 ABI)。

在 C++ 的最新草案中,class.static.mfct#2

删除了对具有相同名称和参数的静态和非静态成员函数的任何限制。


0
投票
[over.over]p4

的有效代码,其中表示:

非成员函数、静态成员函数和显式对象成员函数匹配函数指针类型或函数类型引用的目标。隐式对象成员函数与指向成员函数类型的指针匹配。

clang 和 gcc 产生的错误是损坏错误,请参阅
clang bug:函数签名约束不是损坏名称的一部分

(刚刚修复)和 gcc bug:函数签名约束不是损坏名称的一部分

© www.soinside.com 2019 - 2024. All rights reserved.