0

I have a class with a member called f and simultaneously a generic free function called f. The free function f is meant to called from another member (called g below).

class A{};
int f(A const& a){return 5;} // generic free f

template<class T>
struct B{
    void f() const{} // member f
    int g(){
        T a;
        auto i = f(a) + 1; // here (cannot resolve f)
        return i;
    }
};

int main(){
    B<A> b; 
    int j = b.g();
    assert(j == 6);
}

It turns out that my compilers (GCC and clang) can't resolve the call to f.

error: no matching function for call to ‘B::f(A&)’
   auto i = f(a) + 1;               ^
note: candidate: void B::f() const
  void f() const{}       ^
note:   candidate expects 0 arguments, 1 provided

This is of course because it gets confused with the member f. Yet, only ::f makes sense.

If I were to force the member function, I could say this->f or B::f. However I know no syntax that can do the opposite, i.e. force the free function ::f or actually some external resolution to f given by ADL and disable the member function (something like notthis->f).

I could use using ::f:

... int g(){
        T a;
        using ::f;
        auto i = f(a) + 1;
        return i;
    } ...

But, this wouldn't be generic because I don't know to which namespace T belongs and I want generic code. (here it is :: but for T = A, but for other classes in other namespaces I don't know).

For example, the class B<ns::C> should compile for:

namespace ns{
    class C{};
    int f(C const& a){return 5;} // genetic free f
}

Of course I could rename everything to avoid the clash but that is not an acceptable solution, suppose that it makes sense to have both the free and the member function called f.

Is this a bug on GCC and clang, a defect on the language or am I missing some obvious way to specify to not use the member function?

(the example uses C++11 but the question is valid for any version of C++).


Thanks to @JohnZwinck, this is the current workaround

class A{};
int f(A const& a){return 5;} // genetic free f

class nonmember{
    protected:
    template<class TT> static decltype(auto) _f(TT&& t){return f(std::forward<TT>(t));}
};

template<class T>
struct B : private nonmember{
    void f() const{} // member f
    int g(){
        T a;
        auto i = _f(a) + 1;
        return i;
    }
};
alfC
  • 10,293
  • 4
  • 42
  • 88
  • This is not a bug. It's a deliberate feature of the language - names within a scope hide names from containing scopes. Inside `B::g()` simply specify the scope that contains the function you want. In this case, `auto i = ::f(a) + 1` will give the behaviour you seek. – Peter Dec 16 '17 at 05:54
  • @Peter, ok, but what happens if I don't know in which namespace the function `f` applying to the unknown type `T` is? (I want to deduce that from ADL.) – alfC Dec 16 '17 at 09:03
  • You can't. Candidate matches in this case are found by name, and the rule involves names in the current scope HIDING names in containing scopes. – Peter Dec 16 '17 at 11:12

1 Answers1

2

Is this a bug on GCC and clang, a defect on the language?

None of the above. It is a design decision within the language that member functions whose names match the call are the best match. If you want to avoid them, you can call through an intermediate free function:

template<class T>
int call_f(const T& arg){
    return f(arg);
} 

template<class T>
struct B{
    void f() const{}
    int g(){
        T a;
        auto i = call_f(a) + 1;
        return i;
    }
};
John Zwinck
  • 207,363
  • 31
  • 261
  • 371
  • I would understand this if there is an ambiguity, but in this case only an existing non-member could possibly compile (number of arguments match). – alfC Dec 16 '17 at 01:43
  • `call_f` cannot be a static member function even. Thank you for the workaround. – alfC Dec 16 '17 at 01:44
  • @alfC: There are multiple cases in C++ where only one possibility could compile yet the language designers decided to have the code not compile. The compiler is not expected to try all possibilities first in general (SFINAE is something of an exception to this). With compile times for C++ already being a pet peeve of many, it's not clear that trying all possibilities is practical--not to mention the potential problems with such code being very fragile and hard to reason about. `call_f()` can be a static member function of another class, but not of `B`. – John Zwinck Dec 16 '17 at 02:02
  • Sure, but if `f` where called different the language would actually try such "all possibilities" search. So it is not that the language avoids that at all cost. ADL rules apply there to bound the search. I would say that this is a defect in the language. Any way, yes, the function can be static member of another (and non-nested) class. Maybe one can do the function private and `struct B` a friend to avoid other problems. – alfC Dec 16 '17 at 02:19
  • BTW, this thing will happen more and more often if this "unified call proposal" is accepted. https://isocpp.org/blog/2016/02/a-bit-of-background-for-the-unified-call-proposal – alfC Dec 16 '17 at 02:20
  • I still think it is a defect of the language, however maybe there is a logic behind that is that allowing this would make easy to hijack the behavior of a class by having code that executes with an non-member function with the same name if the class code depends on overload resolution and tricky automatic cast. Not a good enough reason in my opinion but a possible "utility" of this feature. – alfC Dec 16 '17 at 04:07