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

I have been using this code for some time (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 second overload point bar()must be SFINAE'd if Rit is not a type of range where overload exists for its elements bar(). So, it std::vector<int>will be fine, but std::vector<int*>it will not, for example.

Unfortunately, since Clang 3.9, which 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 do this without relying on using an incomplete type from my own definition?

+4
source share
4 answers

, foo :

#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]

foo.

+4

- bar .

#include <utility>

template<typename child>
struct base {
    void bar(int);
};

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

.

, , bar , :

struct foo {
    void bar(int);

    template<typename R,
        std::enable_if_t<std::is_constructible<int, decltype(*std::begin(std::declval<R>()))>>* = 0>
    void bar(const R& range);
};

bar , :

struct foo {
    template<typename T, std::enable_if_t<some_contraint<T>::value>* = 0>
    void bar(T);

    template<typename R,
        std::enable_if_t<some_contraint<*std::begin(std::declval<R>())>::value>* = 0>
    void bar(const R& range);
};

, , :

template<typename, typename = void>
struct is_valid_range : std::false_type {};

template<typename T>
struct is_valid_range<T, std::enable_if_t<some_contraint<*std::begin(std::declval<R>())>::value>> : std::true_type {};
+2

, , , . , , , .

, SFINAE'ing , R ? :

template<typename R,
         class = decltype(begin(std::declval<const R&>())),
         class = decltype(end(std::declval<const R&>()))>
void bar(const R& range);

, , begin end f const R&.

+1
class Foo;
void free_bar(Foo* foo, int n){
  (void)foo;
  std::cout << n << "\n";
}

class Foo {
public:
  template<class X>
  void bar(X&& x) {
    return free_bar( this, std::forward<X>(x) );
  }
};

template <typename R>
auto free_bar(Foo* foo, const R& range)
-> decltype( free_bar( foo, *std::begin(range) ) )
{
  for (auto&&x:range)
    free_bar(foo, decltype(x)(x));
}

bar , Foo* .

.bar(X) .

ADL , (tm) .

+1

Source: https://habr.com/ru/post/1660707/


All Articles