【问题标题】:Overloading static and non-static member function with constraint使用约束重载静态和非静态成员函数
【发布时间】:2022-01-22 12:37:47
【问题描述】:

此代码有效吗?

template<bool b>
struct s {
    void f() const {
    }
    static void f() requires b {
    }
};

void g() {
    s<true>().f();
}

clang 说是,但 gcc 说不是

<source>: In function 'void g()':
<source>:10:20: error: call of overloaded 'f()' is ambiguous
   10 |         s<true>().f();
      |         ~~~~~~~~~~~^~
<source>:3:14: note: candidate: 'void s<b>::f() const [with bool b = true]'
    3 |         void f() const {
      |              ^
<source>:5:21: note: candidate: 'static void s<b>::f() requires  b [with bool b = true]'
    5 |         static void f() requires b {
      |                     ^
Compiler returned: 1

https://godbolt.org/z/f4Kb68aee

【问题讨论】:

  • 让非static 成员void f() const requires(!b) 怎么样?
  • 我相当肯定 gcc 是对的,而 clang 是错的,但是从标准中挖掘一个权威引用将是一件很困难的事情。
  • @TedLyngmo,是的,我可以这样做,但这会导致我在 clang 中的总编译时间显着降低。
  • @DavidStone 哦,不好。顺便说一句,clang 选择了哪个重载?编辑:我检查了。这是有约束的。

标签: c++ language-lawyer c++20 c++-concepts


【解决方案1】:

如果我们通过[over.match.best.general],我们得到:

一个可行的函数F<sub>1</sub>被定义为比另一个可行的函数F<sub>2</sub>更好的函数,如果对于所有参数iICS<sub>i</sub>(F<sub>1</sub>)不是比@987654328更差的转换序列@,然后 [...]

唯一的参数是对象参数,我们之前有:

如果F 是一个静态成员函数,ICS<sub>1</sub>(F) 被定义为对于任何函数GICS<sub>1</sub>(F) 既不比 ICS<sub>1</sub>(G) 好也不差,并且对称地,ICS<sub>1</sub>(G) 既不好也不差比ICS<sub>1</sub>(F)差;否则,

所以前提成立:一个函数的所有参数的转换序列不比另一个函数的转换序列差。所以我们继续我们的决胜局......

  • 对于某些参数jICS<sub>j</sub>(F<sub>1</sub>) 是比ICS<sub>j</sub>(F<sub>2</sub>) 更好的转换顺序,或者,如果不是这样,

我们可以有一个更好的转换序列的唯一参数是对象参数,并且已经确定,它是等价的。所以这个决胜局不适用。

  • 上下文是通过用户定义的转换进行的初始化(参见 [dcl.init]、[over.match.conv] 和 [over.match.ref])和 [...]

没有。

  • 上下文是由转换函数初始化的,用于直接引用绑定到函数类型的引用,[...]

没有。

  • F1 不是函数模板特化,F2 是函数模板特化,或者,如果不是,

没有。

  • F1F2 是函数模板特化,根据 [temp.func.order] 中描述的偏序规则,F1 的函数模板比​​ F2 的模板更特化,或者,如果不是那,

没有。

  • F1F2 是具有相同参数类型列表的非模板函数,并且根据 [temp.constr.order] 中描述的约束的部分排序,F1F2 更受约束,或者如果不是这样,

啊哈!在此示例中,我们有具有相同参数类型列表的非模板函数(两者都是空的)。静态成员函数是受约束的,非静态成员函数是不受约束的,这是最琐碎的一种“更受约束”(见[temp.constr.order])。

因此,我认为clang(和msvc)接受程序是正确的,而gcc拒绝它是不正确的。 (已提交103783)。

【讨论】:

    【解决方案2】:

    根据 C++20 标准 class.static.mfct#2,您的代码格式不正确:

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

    这里没有例外,requires-clause 的存在是为了区分成员函数,只有相同的名称和相同的参数类型。这正是我们的情况:同名是f,同样的参数类型是空集。

    所以 Clang 和 MSVC 在接受代码时是错误的。但是 GCC 的诊断肯定令人困惑。

    通过对代码进行一些小的调整(在非静态成员函数中删除 const 并在代码中获取其地址),Clang 和 MSVC 也显示出有很大的问题:

    template<bool b>
    struct s {
        void f() {}
        static void f() requires b {}
    };
    
    int main() {
        s<true>().f();
        void (s<true>::*x)() = &s<true>::f;
    }
    

    演示:https://gcc.godbolt.org/z/vdq9j63Gs

    【讨论】:

    • 看起来该措辞已更改为非规范性注释并在 N4868 中扩展为:“不能有同名的静态和非静态成员函数,参数类型-列表和尾随的要求子句([over.load])。”。 timsong-cpp.github.io/cppwp/n4868/class.static.mfct#2。标准的更高版本完全删除了这句话。
    • @DavidStone,谢谢,我以为 N4861 是最新的 C++20 愚蠢,而 N4868 已经是 C++23 草案:github.com/cplusplus/draft/releases/tag/n4868
    • 哦,好点。我将“Pre-Fall 2020 C++ 工作草案”误读为“Pre-Fall C++20 工作草案”。但是,鉴于它应该只包含编辑性更改,我怀疑这只是一个没有针对概念更新的措辞错误修复?
    猜你喜欢
    • 1970-01-01
    • 2014-04-14
    • 1970-01-01
    • 1970-01-01
    • 2011-07-18
    • 1970-01-01
    • 2011-05-31
    相关资源
    最近更新 更多