Tavian Barnes Tavian Barnes - 22 days ago 4
C++ Question

Is there a way to SFINAE based on available overloads in the current class?

I've been using code like this for a while (since GCC 4.9/Clang 3.5 at least):

#include <utility>

class foo
{
public:
void bar(int n);

template <typename R,
typename = decltype(std::declval<foo>().bar(*std::begin(std::declval<R>())))>
void bar(const R& range);
};


The point of the second
bar()
overload is it should be SFINAE'd away unless
R
is a range type where an overload of
bar()
exists for its elements. So
std::vector<int>
would be fine but
std::vector<int*>
wouldn't, for example.

Unfortunately, since Clang 3.9, that gives this error:

templ.cpp:12:54: error: member access into incomplete type 'foo'
typename = decltype(std::declval<foo>().bar(*std::begin(std::declval<R>())))>
^
templ.cpp:6:7: note: definition of 'foo' is not complete until the closing '}'
class foo
^
1 error generated.


Is there a way to accomplish this that doesn't rely on using an incomplete type from within its own definition?

Answer

Maybe you could make foo a default value of additional template parameter:

#include <utility>

class foo
{
public:
    void bar(int n);

    template <typename R,
              typename F = foo,
              typename = decltype(std::declval<F>().bar(*std::begin(std::declval<R>())))>
    void bar(const R& range);
};

[live demo]

This would delay the check if foo is complete.