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
There is namespace.udecl, item 12 (emphasis mine):
For the purpose of forming a set of candidates during overload resolution, the functions named by a using-declaration in a derived class are treated as though they were direct members of the derived class. [...] This has no effect on the type of the function, and in all other respects the function remains part of the base class.
Thus, a
is not a member of B
, and the type of &B::a
is int (A::*)()
.
(&B::a
means the same thing regardless of whether you include using 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.