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?
A template function can be overloaded either by a non-template function or using an ordinary function template.
Both function overloading and templates are examples of polymorphism features of OOP. Function overloading is used when multiple functions do quite similar (not identical) operations, templates are used when multiple functions do identical operations.
Explanation: Template function cannot be overloaded as done in this program.
The term member template refers to both member function templates and nested class templates. Member function templates are function templates that are members of a class or class template. Member functions can be function templates in several contexts.
This conundrum was recently explained to me in the light of another answer.
From the #clang IRC channel:
[01:16:23] <zygoloid> Xeo: this is a weird corner of the language where clang conforms but the rule is silly
[01:16:31] <Xeo> ... really? :(
[01:16:45] <zygoloid> Xeo: when deciding whether a using-declaration is hidden, we're not allowed to look at the template-parameter-list (nor the return type, iirc)
[01:17:04] <zygoloid> so the derived class declaration of operator()(T) suppresses the using-declaration
[01:17:19] <Xeo> because it has the same signature / parameter types?
[01:17:40] <zygoloid> rigth
The workaround is to not define f
in the class that uses
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:
@Xeo fixed it anyways, for some reason this form is not objectionable to clang++ stackoverflow.com/a/18432618/85371
Credits Thanks to @Xeo and people in the Lounge for unearthing this "silly rule"
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.
template <typename Y>
struct X
{
template <typename> static bool f() { return true; }
template <typename> bool g() { return true; }
template <int I>
static bool f() { return Y::template _f <I>(); }
template <int I>
bool g()
{
return static_cast <Y&>(*this).template _g <I>();
}
};
class Y : public X <Y>
{
friend class X <Y>;
template <int> static bool _f() { return false; }
template <int> bool _g() { return false; }
};
int main()
{
Y y;
std::cout << Y::f <void>() << " " << Y::f <0>() << std::endl;
std::cout << y. g <void>() << " " << y. g <0>() << std::endl;
}
So all overloading takes place in base class X
, which implements static polymorphism by taking Y
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 like Y
with only one overload in each, and a single base class X
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 this decltype
only works in gcc. Enabling c++1y one only writes auto g()
without the trailing return type specification, thus avoiding the problem with decltype
. 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 of Y
's methods by hand, which may be painful especially if there are lots of Y
s.
It still looks like a mess to me, but at least not a complete one.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With