Name conflict between template struct and template member function
Asked Answered
S

3

7

In the following, GCC confuses template struct name with template member function name of class A, while Clang compiles fine (live example):

template<typename T>
struct name {};

struct A
{
    template<bool B>
    void name() { }
};

template<bool B, typename T>
void f(T& x) { x.template name<B>(); }

Function f is apparently meant to be called with an argument of type A in this example, but it could be anything else, so f needs to remain a template function.

I don't care much which compiler is correct, I only need a work-around because I really don't know any syntax other than

x.template name<B>();

to call the member function, and I cannot see how a using declaration or any other way of disambiguation could apply.

EDIT Yes, I now tried the more explicit syntax

x.T::template name<B>();

which works, but is really ugly. Any way to make the brief syntax work? Otherwise, it might be preferable to change one of the two names to begin with...

EDIT2 My original version of f works on a universal reference T&&, which needs the ugliest

using X = typename std::remove_reference<T>::type;
x.X::template name<B>();

in case T is a reference... And all this for a simple function call.

Skyway answered 11/3, 2014 at 0:23 Comment(6)
Work around: write a free function that takes a A and invokes a.name<B>? Override for &, const&, and && if needed.Highspeed
@Yakk Well, isn't f exactly this function? Oh, you mean take an A and not a template argument? This is nearly impossible, it could be called with anything... A was just an example.Skyway
Why not just write void f(T& x) { x.name<B>(); } ?Edva
@Edva Have you ever tried this? Doesn't work when x's type, T, is unknown. When f is first parsed, how would the compiler know that name isn't a plain data member of x? This would give expression x.name followed by operator < followed by B ( just a bool), followed by operator >, followed by empty pararetheses, at which point you get expected primary-expression. Besides, in this example, ::name gets in the way before all of this happens.Skyway
@iavr: Maybe there is something I missunderstand with your problem, but yes, I've tested it with VS2013 (see my answer) and I see no reason, why it wouldn't work with gcc or clangEdva
To answer your Question, why the compiler should know that name isn't a plain data member of x: It knows this, because at the point where f is instantiated, it has to know the definition of T anyway and thus knows that name is a templated member function.Edva
C
1

I've thought about this a bit and I can't see any way to make the most basic syntax you want work, due to all the templates involved. The reason you have to specify template is because otherwise it looks to the compiler like you are using < to compare the address of the function to B.

As you said, you could simply rename one of the name identifiers, allowing the compiler to have no ambiguity as to which one you mean.

Alternately you could do exactly what you said and fully qualify the call. I don't find this to be ugly syntax: it's perfectly clear to the reader exactly what's going on, and after all you only have to write the function one time.

template<bool B, typename T>
void f(T& x)
{
    typedef typename std::remove_reference<T>::type callee;

    x.callee::template name<B>();
}

Finally if you could elaborate a little more on the real problem you're trying to solve with this template we might be able to offer an orthogonal solution that doesn't involve such type aliasing at all.

Cindiecindra answered 20/6, 2014 at 18:47 Comment(0)
E
0

EDIT: As pointed out by Constructor and iavr, the following is non standard behavior:


With VS2013 the following works:

template<typename T>
struct name {};

struct A
{
    template<bool B>
    void name() {
        std::cout << "A::name() called with B=" << B<< std::endl;
    }
};

template<bool B, typename T>
void f(T& x) { x.name<B>(); }

int main(){
    A a;
    f<true>(a);
}

output:

 A::name() called with B=1
Edva answered 22/3, 2014 at 10:47 Comment(5)
Thanks. This doesn't help me much because (1) linux, (2) limited support for c++11. It doesn't also say much on whether this is the expected behaviour according to the standard. As far as I know, x.name<B>() should not work, regardless of ::name.Skyway
I just wanted to suggest you try it this way and see if it helps. I would have expected this to conform to the standard as it seems natural to me, but that was just a guess. However, as you apparently have to be compatible to a specific version of gcc (which one?) it's probably not important, whether it conforms to the standard, but only if this version of gcc can compile it - so again: try and find out.Edva
It is non-standard behavior. See paragraph [temp.names] 14.2/4 and 14.2/5 of the standard.Pentecostal
@Pentecostal Thanks. I would not follow any non-standard workaround. I am just not quite familiar with the standard :-)Skyway
Thanks for clarification, I added a note to my answerEdva
H
0

You can also use namespace.

Put the "struct name" in a namespace, and then refer to struct name as NS::name.

namespace NS
{
    template<typename T>
    struct name {};
}

You can use your original x.template name() with such method.

Harim answered 28/7, 2014 at 2:2 Comment(2)
From a practical point of view, this is no different than just renaming struct name. When I made the question, apparently this was not an option.Skyway
Hi @iavr, There is difference. If the template definition is in a header, you can use namespace to enclose it when you include it. Then you solve those naming related problems without modifying the header. This is how C++ manages inclusion of old C headers. On the other hand, if you can change the template name, why don't you just change it?Harim

© 2022 - 2024 — McMap. All rights reserved.