应该使用引入一个新的成员函数还是只是一个别名?

Shall using introduce a new member function or just an alias?

以下最小示例可以在 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)'

这很有趣。

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

尽管重载解析现在可以在 B:

中找到成员

[namespace.udecl/15]: [Note: For the purpose of forming a set of candidates during overload resolution, the functions that are introduced by a using-declaration into a derived class are treated as though they were members of the derived class ([class.member.lookup]). In particular, the implicit object parameter is treated as if it were a reference to the derived class rather than to the base class ([over.match.funcs]). This has no effect on the type of the function, and in all other respects the function remains a member of the base class. — end note]

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

[expr.prim.id.qual/2]: A nested-name-specifier that denotes a class, optionally followed by the keyword template ([temp.names]), and then followed by the name of a member of either that class ([class.mem]) or one of its base classes, is a qualified-id; [class.qual] describes name lookup for class members that appear in qualified-ids. The result is the member. The type of the result is the type of the member. [..]

但是成员的实际类型因此是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中还有一个兼容模式可能是罪魁祸首。