In the following, struct Y
overloads X
's member function f
. Both overloads are template functions, but take different arguments (typename
and int
), to be explicitly specified:
struct X
{
template <typename> static bool f() { return true; }
};
struct Y : public X
{
using X::f;
template <int> static bool f() { return false; }
};
int main()
{
std::cout << Y::f <void>() << " " << Y::f <0>() << std::endl;
}
This prints 1 0
using gcc, as expected. However, clang (3.3) complains that
[...] error: no matching function for call to 'f'
std::cout << Y::f <void>() << " " << Y::f <0>() << std::endl;
^~~~~~~~~~~
[...] note: candidate template ignored: invalid explicitly-specified argument
for 1st template parameter
template <int> static bool f() { return false; }
^
i.e., can only see Y
's version. I've tried
using X::template f;
instead, with no success. The same happens for non-static (template) member functions. So is this a bug?
It comes as a huge disappointment that such a constraint exists and has not been relaxed in C++11 (there may be good reason but I cannot imagine why). I feel like it defeats the whole concept of class hierarchies.
Anyhow, here's one workaround I have found. I have included another function
g
that is non-static to illustrate the differences, because this case is my main interest.So all overloading takes place in base class
X
, which implements static polymorphism by takingY
as a template argument (fortunately, this was already the case in my project so I do not change the design).The actual
Y
's implementations are in private functions_f
,_g
. This design is good when there are many derived classes likeY
with only one overload in each, and a single base classX
with multiple other overloads. In this case, massive code duplication is avoided. Again, this is the case in my project.X
does not need to know the return value of these functions. Unfortunately, it does need to know the return type: I have tried e.g.auto g() -> decltype(...)
and again thisdecltype
only works in gcc. Enabling c++1y one only writesauto g()
without the trailing return type specification, thus avoiding the problem withdecltype
. However, clang's support for "return type deduction for normal functions" (N3638) is only available in current SVN version.Until
auto g()
becomes mainstream (and standard), one has to compute the return type ofY
's methods by hand, which may be painful especially if there are lots ofY
s.It still looks like a mess to me, but at least not a complete one.
This conundrum was recently explained to me in the light of another answer.
From the #clang IRC channel:
The workaround is to not define
f
in the class thatuses
the derived version. Instead, move it into an auxiliary helper class (which, in this case begs the question, which definition you reckon should win).See here for my earlier problematic case: Lambda functions as base classes
And here how to fix it using an extra base-class:
Credits Thanks to @Xeo and people in the Lounge for unearthing this "silly rule"