Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

using and overloading a template member function of a base class?

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?

like image 747
iavr Avatar asked Sep 17 '13 23:09

iavr


People also ask

Can you overload a template function?

A template function can be overloaded either by a non-template function or using an ordinary function template.

How function overloading and function templates are related to each other?

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.

Can we have overloading of the function templates Mcq?

Explanation: Template function cannot be overloaded as done in this program.

What is member function template in C++?

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.


2 Answers

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).

Credits Thanks to @Xeo and people in the Lounge for unearthing this "silly rule"

like image 181
sehe Avatar answered Sep 29 '22 05:09

sehe


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 Ys.

It still looks like a mess to me, but at least not a complete one.

like image 41
iavr Avatar answered Sep 29 '22 07:09

iavr