使用父级方法作为派生类方法时的海湾频道错误
我的代码中有一个功能,该功能仅接受类成员方法作为模板参数。我需要使用从父类继承的类方法调用此方法。这是我问题的示例代码:
template <class C>
class Test {
public:
template<typename R, R( C::* TMethod )()> // only a member function should be accepted here
void test() {}
};
class A {
public:
int a() { return 0; } // dummy method for inheritance
};
class B : public A {
public:
using A::a; // A::a should be declared in the B class declaration region
// int a() { return A::a(); } // if this lines is activated compliation works
};
int main() {
auto t = Test<B>();
t.test<int, &B::a>();
}
使用MSVC 2019编译器,无问题编译代码。但是,海湾合作委员会会产生以下错误:
<source>: In function 'int main()':
<source>:23:23: error: no matching function for call to 'Test<B>::test<int, &A::a>()'
23 | t.test<int, &B::a>();
| ~~~~~~~~~~~~~~~~~~^~
<source>:5:10: note: candidate: 'template<class R, R (B::* TMethod)()> void Test<C>::test() [with R (C::* TMethod)() = R; C = B]'
5 | void test() {}
| ^~~~
<source>:5:10: note: template argument deduction/substitution failed:
<source>:23:17: error: could not convert template argument '&A::a' from 'int (A::*)()' to 'int (B::*)()'
23 | t.test<int, &B::a>();
|
据我了解,海湾合作委员会仍在处理B :: A作为A :: A的类型。在CPP中引用了它的说法
将其他位置定义的名称引入声明性 出现这种使用量的区域。
因此,我认为使用应将A ::一种方法转移到B的Declerativ区域,因此应将其处理为B :: a。我错了还是海湾合作委员会中有错误?
这是编译器资源管理器上的示例: https://godbolt.org.org/z/ttrd189swssw
I have a function in my code which only accepts a class member method as a template parameter. I need to call this method using a class method which is inherited from a parent class. Here is an example code of my problem:
template <class C>
class Test {
public:
template<typename R, R( C::* TMethod )()> // only a member function should be accepted here
void test() {}
};
class A {
public:
int a() { return 0; } // dummy method for inheritance
};
class B : public A {
public:
using A::a; // A::a should be declared in the B class declaration region
// int a() { return A::a(); } // if this lines is activated compliation works
};
int main() {
auto t = Test<B>();
t.test<int, &B::a>();
}
With the MSVC 2019 compiler the code compiles without problems. However the gcc produces following error:
<source>: In function 'int main()':
<source>:23:23: error: no matching function for call to 'Test<B>::test<int, &A::a>()'
23 | t.test<int, &B::a>();
| ~~~~~~~~~~~~~~~~~~^~
<source>:5:10: note: candidate: 'template<class R, R (B::* TMethod)()> void Test<C>::test() [with R (C::* TMethod)() = R; C = B]'
5 | void test() {}
| ^~~~
<source>:5:10: note: template argument deduction/substitution failed:
<source>:23:17: error: could not convert template argument '&A::a' from 'int (A::*)()' to 'int (B::*)()'
23 | t.test<int, &B::a>();
|
As far as I understand the gcc is still handling the type of B::a as A::a. On the cpp reference its saying that using
Introduces a name that is defined elsewhere into the declarative
region where this using-declaration appears.
So in my opinion the using should transfer the A::a method to the declerativ region of B and therefor it should be handled as B::a. Am I wrong or is there a bug in GCC?
Here is the example on Compiler Explorer: https://godbolt.org/z/TTrd189sW
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
有 namespace.udecl ,项目12,项目12(重点是我的):
因此,
a
不是b
的成员,&amp; b :: a
的类型是 INSint(a ::::: *)()
。(
&amp; b :: a
无论您是否包括使用a :: a;
>)表示同一件事,使用没有意义代码>来自基类的命名函数,除了要超载或覆盖它们时围绕“隐藏问题”工作。
There is namespace.udecl, item 12 (emphasis mine):
Thus,
a
is not a member ofB
, and the type of&B::a
isint (A::*)()
.(
&B::a
means the same thing regardless of whether you includeusing A::a;
or not)There is no point to
using
named functions from a base class except to work around the "hiding problem" when you want to overload or override them.(non-nullptr)在转换的常数表达式中不允许使用指针到会员转换
您错了,但是我们需要稍微降低语言规则兔子孔以找出原因。
首先,即使是通过派生类引用的指针的 type (即使是通过使用声明引入)也是指针到成员的基础。 ) ]/3 明确涵盖此用例:
但是 。 (衍生的):
也就是说,可以将基础类成员的指针转换为派生类的成员,实际上是以下程序,其中转换是在参数(基本的指针到成员)的上下文中进行函数参数的(类型:派生的指针到成员)是良好的:
为什么模板参数的情况失败?一个最小的示例是:
根本原因是模板参数扣除失败:模板参数为
&amp; a :: a
typeint(a ::**)()
和适用:在转换的常数表达式中不允许A(非零PTR)指针到会员转换([[Cons.Mem]/2)(请参阅 [expr.const]/10 ),意思类型
int(b ::*)()()
的非类型模板参数。我们可能会注意到,如果我们更改为类模板,Clang实际上为我们提供了非常清晰的诊断:
(Non-nullptr) pointer-to-member conversions are not allowed in converted constant expressions
You are wrong, but we'll need to go a bit down the language rules rabbit hole to find out why.
First of all the type of the pointer to member, even when referred to via the derived class (even when introduced via a using declaration), is that of pointer-to-member-of-base. The (non-normative) example of [expr.unary.op]/3 explicitly covers this use case:
However [conv.mem]/2 covers that you may convert
int (A::*)()
(base) toint (B::*)()
(derived):Namely, a pointer to member of a base class may be converted to member of derived class, and indeed, the following program, where conversion is made in the context of an argument (pointer-to-member of base) to a function parameter (type: pointer-to-member of derived) is well-formed:
Then why does the case of a template parameter fails? A more minimal example is:
The root cause is that template argument deduction fails: the template argument is
&A::a
of typeint(A::*)()
and [temp.arg.nontype]/2 applies:A (non-nullptr) pointer-to-member conversion ([conv.mem]/2) is not allowed in a converted constant expression (refer to [expr.const]/10), meaning
&A::a
is not a valid template argument to a non-type template parameter of typeint(B::*)()
.We may note that Clang actually gives us a very clear diagnostic on this if we change to class template: