【问题标题】:Shall using introduce a new member function or just an alias?应该使用引入一个新的成员函数还是只是一个别名?
【发布时间】:2020-07-17 09:06:52
【问题描述】:

以下最小示例在 MSVC 17 上可以正常编译,但在 GCC 8.2 上会产生编译错误。哪个编译器是对的?这段代码在 C++17 中是否正确?

#include <iostream>

class A
{
public:

    A() = default;

protected:

    void foo(int x)
    { std::cout << x << std::endl; }
};

class B : private A
{
    using Method_t = void (B::*)(int);
    using A::foo;

    template <Method_t M>
    void baz()
    { (this->*M)(42); }

public:

    B() = default;

    void bar()
    { baz<&B::foo>(); }
};

int main()
{
    B().bar();
}

GCC 错误是:

mwe.cpp:29:20: error: could not convert template argument '&A::foo' from 'void (A::*)(int)' to 'void (B::*)(int)'

【问题讨论】:

    标签: c++ gcc visual-studio-2017 c++17 language-lawyer


    【解决方案1】:

    这很有趣。

    根据当前规则*,似乎是the intent is for foo to remain a member of the base,而不是引入B 的实际成员。

    尽管重载解析现在可以在B 中找到成员:

    [namespace.udecl/15]: [注意: 为了在重载决议期间形成一组候选者,using-declaration 引入的函数到派生类被视为派生类的成员([class.member.lookup])。特别是,隐式对象参数被视为对派生类的引用,而不是对基类的引用 ([over.match.funcs])。 这对函数的类型没有影响,并且在所有其他方面,该函数仍然是基类的成员。 —结束注释]

    尽管事实上,在代码中,B::bar can refer to that member(即不必拼写为 A::bar):

    [expr.prim.id.qual/2]一个nested-name-specifier,表示一个类,可选地后跟关键字template ([temp.names]),然后后跟该类 ([class.mem]) 或其基类之一的成员的名称是一个qualified-id; [class.qual] 描述了对出现在 qualified-ids 中的类成员的名称查找。 结果是会员。结果的类型就是成员的类型。 [..]

    但成员的实际类型因此是void (A::*)(int)

    没有规则允许转换为void (B::*)(int),即使是特定于以这种方式引入的成员的规则(显然这种转换一般来说是无效的)。

    因此,我认为 Visual Studio 是错误的。

    * 为了方便起见,我引用了当前的草案,但没有理由相信这条规则最近发生了变化; GCC 和 Clang 都拒绝所有 C++11、C++14 和 C++17 中的代码。


    顺便说一句,this doesn't actually compile with the latest version of Visual Studio, either:

    <source>(29): error C2672: 'B::baz': no matching overloaded function found
    <source>(29): error C2893: Failed to specialize function template 'void B::baz(void)'
    <source>(21): note: see declaration of 'B::baz'
    <source>(29): note: With the following template arguments:
    <source>(29): note: 'M=void A::foo(int)'
    

    所以,也许他们已经修复了自您的版本以来的错误。 VS中还有一个兼容模式可能是罪魁祸首。

    【讨论】:

    • 可以隐式地将基类的指向成员的指针转换为派生的指向成员的指针——毕竟,派生类具有基类的所有成员.但是,对于 已转换的常量表达式,不允许进行这种转换。
    猜你喜欢
    • 1970-01-01
    • 2011-01-18
    • 2023-03-30
    • 2012-09-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-01
    • 1970-01-01
    相关资源
    最近更新 更多