应该使用引入一个新的成员函数还是只是一个别名?
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中还有一个兼容模式可能是罪魁祸首。
以下最小示例可以在 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 keywordtemplate
([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中还有一个兼容模式可能是罪魁祸首。