Jos*_*eph 5 c++ gcc language-lawyer c++17 visual-studio-2017
下面的最小示例在 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();
}
Run Code Online (Sandbox Code Playgroud)
GCC 错误是:
mwe.cpp:29:20: error: could not convert template argument '&A::foo' from 'void (A::*)(int)' to 'void (B::*)(int)'
Run Code Online (Sandbox Code Playgroud)
这很有趣。
\n根据当前规则*,其目的似乎是foo保留基地的成员,而不是引入实际的成员B。
尽管事实上重载解析现在可以在中找到该成员B:
\n\n\n
[namespace.udecl/15]:[注意:为了在重载解析期间形成一组候选者,由using 声明引入派生类的函数将被视为派生类的成员 ([class.member.lookup] )。特别是,隐式对象参数被视为对派生类而不是基类的引用 ([over.match.funcs])。这对函数的类型没有影响,并且在所有其他方面该函数仍然是基类的成员。 \xe2\x80\x94 尾注]
尽管事实上,在代码中,B::bar可以引用该成员(即不必拼写为A::bar):
\n\n\n
[expr.prim.id.qual/2]:表示类的嵌套名称说明符,可以选择后跟关键字template([temp.names]),然后后跟该类 ([class.mem]) 或其基类之一的成员的名称类,是一个合格的 id;[class.qual] 描述出现在qualified-ids中的类成员的名称查找。结果就是会员。结果的类型是成员的类型。 [..]
但成员的实际类型是void (A::*)(int)。
没有规则允许转换为void (B::*)(int),即使是专门针对以这种方式引入的成员的规则(显然这种转换通常是无效的)。
因此,我认为 Visual Studio 是错误的。
\n* 为了方便起见,我引用了当前的草案,但没有理由相信该规则最近发生了变化;GCC 和 Clang 都拒绝所有 C++11、C++14 和 C++17 中的代码。
\n顺便说一句,这实际上也不能使用最新版本的 Visual Studio 进行编译:
\n<source>(29): error C2672: \'B::baz\': no matching overloaded function found\n<source>(29): error C2893: Failed to specialize function template \'void B::baz(void)\'\n<source>(21): note: see declaration of \'B::baz\'\n<source>(29): note: With the following template arguments:\n<source>(29): note: \'M=void A::foo(int)\'\nRun Code Online (Sandbox Code Playgroud)\n所以,也许他们已经修复了自您的版本以来的错误。VS 中的兼容模式也可能是罪魁祸首。
\n| 归档时间: |
|
| 查看次数: |
79 次 |
| 最近记录: |