2

I'm not sure if this has anything to do with sfinae, or just something thats relevant for any templated function. I am attempting to use sfinae to enable/disable a member function based on existence of corresponding free function, which in turn is enabled/disabled based on existance of member function in another type, all using method described here:

struct S;

template <typename T>
inline auto f(S& s, T const& t)
   -> decltype(t.f(s), void())
{
   t.f(s);
}

struct S
{
    template <typename T>
    auto f(T const& t)
        -> decltype(f(*this, t), void())
    {
        f(*this, t); // <------------------------------------------- HERE
    }
};

struct pass
{
    void f(S&) const
    {
        //...
    }
};

struct fail
{
};

int main()
{
    S s;
    s.f(pass()); // should compile fine
    //s.f(fail()); // should fail to compile due to absence of f from S
    return 0;
}

however gcc 4.7.1 gives me this on the line marked by arrow:

error: no matching function for call to 'S::f(S&, const pass&)'
note: candidate is:
note: template decltype ((f((* this), t), void())) S::f(const T&)
note: template argument deduction/substitution failed:
note: candidate expects 1 argument, 2 provided

which apparently means that global f above is not considered for overload resolution.

Why is that and what do I do to make it do so?

Also why are there no errors two lines above that, where f used in decltype in similar fashion?

UPDATE

As @n.m. said, member functions completely shadow free functions, even when their signatures are different, so here is a workaround that doesn't break ADL for f (unlike the full name qualification suggested by @n.m. ). Make free function (f_dispatcher) somewhere nobody will look (detail), and fully qualify its name inside S::f. In that function call free f and let ADL take care of it from there onwards, like so:

struct S;

template <typename T>
inline auto f(S& s, T const& t)
    -> decltype(t.f(s), void())
{
    t.f(s);
}

namespace detail
{
    template <typename T>
    inline auto f_dispatcher(S& s, T const& t)
        -> decltype(f(s, t), void())
    {
        f(s, t);
    }
}

struct S
{
    template <typename T>
    auto f(T const& t)
        -> decltype(detail::f_dispatcher(*this, t), void())
    {
        detail::f_dispatcher(*this, t);
    }
};

struct pass
{
    void f(S&) const
    {
        //...
    }
};

struct fail
{
};

int main()
{
    S s;
    s.f(pass()); // compiles fine
    //s.f(fail()); // fails to compile due to absence of f from S
    return 0;
}
Community
  • 1
  • 1
yuri kilochek
  • 12,709
  • 2
  • 32
  • 59
  • Couldn't you use a using-declaration inside `S::f` as well? – dyp Jan 12 '14 at 12:29
  • @dyp, I can, but that won't make it visible in trailing return type. – yuri kilochek Jan 12 '14 at 12:44
  • The trailing-return-type is part of the declaration, so name lookup inside it won't find the declaration it's part of. – dyp Jan 12 '14 at 13:02
  • @dyp, you are right, it works as intended. – yuri kilochek Jan 12 '14 at 13:08
  • (Note that it has some downsides: it's not very reliable as introducing overloads or member functions of the same name inside base classes will break it. C++1y's return type deduction can be used instead.) – dyp Jan 12 '14 at 13:12
  • @dyp return type deduction doesnt allow sfinae. – yuri kilochek Jan 12 '14 at 13:15
  • Oops, right, you wanted to use it for SFINAE. Well then it might part of the yet unsolved ADL problem. Similar problems occur for `noexcept`-specifiers. One suggestion was to allow a using-declaration inside `decltype` etc., but IIRC it wasn't accepted. – dyp Jan 12 '14 at 13:31

1 Answers1

7

This has nothing to do with SFINAE or templates or C++11 or ADL.

A member shadows all non-members with the same name, regardless of type. If you have a member named f, you cannot refer to any non-member named f, unless you use a qualified name (e.g. ::f).

Just use ::f(*this, t);.

n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243