'using' declaration as SFINAE
Asked Answered
B

2

7

Could I use SFINAE (or another technique) for using declaration while private deriving from template class? For better understanding see code below:

#include <iostream>

struct S1 {
    void f() { std::cout << "S1::f\n"; }
};

struct S2 {
    void f() { std::cout << "S2::f\n"; }
    void g() { std::cout << "S2::g\n"; }
};

template <class T>
struct D : private T {
    using T::f;
    // using T::g; // need this only if T provides g() function
};

int main() {
    D<S1>().f(); // ok. Prints 'S1::f'
    D<S2>().f(); // ok. Prints 'S2::f' 
    D<S2>().g(); // fail. But wants to be ok and prints 'S2::g'
    return 0;
}

How can I reach desired behaviour (if it possible)?

Brendon answered 30/1, 2015 at 9:24 Comment(2)
possible duplicate of Is it possible to write a C++ template to check for a function's existence?Bs
@Bs No, this is NOT a duplicate. This question can easily assume we already have the trait has_function_named_g<T>. Now what? How do you apply it to the using declaration?Figge
D
4

C++ partial template specialization and use decltype(void(&T::g)) for SFINAE

#include <iostream>
#include <type_traits>

struct S1 {
    void f() { std::cout << "S1::f\n"; }
};

struct S2 {
    void f() { std::cout << "S2::f\n"; }
    void g() { std::cout << "S2::g\n"; }
};

template <class T, class V = void>
struct D : private T {
    using T::f;
};

template <class T>
struct D<T, decltype(void(&T::g))> : private T {
    using T::f;
    using T::g; // need this only if T provides g() function
};

int main() {
    D<S1>().f(); // ok. Prints 'S1::f'
    D<S2>().f(); // ok. Prints 'S2::f' 
    D<S2>().g(); // ok. Prints 'S2::g'
    return 0;
}

Live Demo


Edit:

This is another approach that is more flexible, but I have no idea how does private virtual inheritance works with real use cases. Please let me know if it may cause any issue (e.g. UB).

#include <iostream>
#include <type_traits>

struct S1 {
    void f() { std::cout << "S1::f\n"; }
};

struct S2 {
    void f() { std::cout << "S2::f\n"; }
    void g() { std::cout << "S2::g\n"; }
};

struct S3 {
    void g() { std::cout << "S3::g\n"; }
};

template <class T, class = void>
struct D_f {};

template <class T>
struct D_f<T, decltype(void(&T::f))> : private virtual T {
    using T::f;
};

template <class T, class = void>
struct D_g {};

template <class T>
struct D_g<T, decltype(void(&T::g))> : private virtual T {
    using T::g;
};

template <class T>
struct D : D_f<T>, D_g<T> {
};


int main() {
    D<S1>().f();
    D<S2>().f();
    D<S2>().g();
    D<S3>().g();
    return 0;
}

Live Demo

Downward answered 30/1, 2015 at 9:36 Comment(3)
This doesn't scale well, though. For n functions that may or may not be available, you need to spell out 2**n class definitions.Corral
@BryanChen Is it possible to limit the change inside main D class template, ie, without the addition of specialization and duplication of code like using T::f; ?Dismissal
I have exactly the same problem except that my structs don't inherit from T, T is only a template variable. @BryanChen as soon as I remove the inheritance from your solution it no longer works. Do you know why, and what can be done to circumvent this?Advisory
C
5

A variant of Bryan Chen's answer that looks uglier, but makes it easier to extend to multiple checks, and doesn't require duplicating the code that's shared between D<type-with-f> and D<type-without-f>, is to use an inheritance chain, where each step checks one additional member. The only duplication required is the inheritance of constructors, if appropriate.

struct A {
  void f() { }
  void g() { }
  void i() { }
};

// The generic case. D<T, char[N]> simply provides what D<T, char[N+1]> provides.
template <typename T, typename U = char[1]>
struct D : D<T, char[sizeof(U) + 1]> {
  using D<T, char[sizeof(U) + 1]>::D;
};

// The end of the chain. This is where T gets inherited. It declares all of its own
// specialisations as its friends, so that they can access other members of T.
template <typename T>
struct D<T, char[6]> : private T {
  template <typename, typename>
  friend struct D;

  D(int) { }
  void fun() { }
};

// Check for T::f.
template <typename T>
struct D<T, char[2 + !sizeof(&T::f)]> : D<T, char[3]> {
  using D<T, char[3]>::D;
  using T::f;
};

// Check for T::g.
template <typename T>
struct D<T, char[3 + !sizeof(&T::g)]> : D<T, char[4]> {
  using D<T, char[4]>::D;
  using T::g;
};

// Check for T::h.
template <typename T>
struct D<T, char[4 + !sizeof(&T::h)]> : D<T, char[5]> {
  using D<T, char[5]>::D;
  using T::h;
};

// Check for T::i.
template <typename T>
struct D<T, char[5 + !sizeof(&T::i)]> : D<T, char[6]> {
  using D<T, char[6]>::D;
  using T::i;
};

int main() {
  D<A> d = 4; // ok: verify that constructors got inherited
  // A &a = d; // error: verify that inheritance of A is private
  d.f(); // ok: verify that f got inherited
  d.g(); // ok: verify that g got inherited
  // d.h(); // error: verify that h is not available
  d.i(); // ok: verify that i got inherited
  d.fun(); // ok: verify that the inheritance chain didn't get broken
}

Note: instead of checking &T::f, you may want to do something with std::declval<T>().f() instead. The former cannot handle overloaded functions.

Corral answered 30/1, 2015 at 12:15 Comment(2)
Hmm, I might have used std::integral_constant, but very nice.Angadreme
@Angadreme Heh, that's one of the things I know is there, but somehow never end up using, because whenever it could be useful, I've already ended up with a solution in my mind that doesn't need it. :)Corral
D
4

C++ partial template specialization and use decltype(void(&T::g)) for SFINAE

#include <iostream>
#include <type_traits>

struct S1 {
    void f() { std::cout << "S1::f\n"; }
};

struct S2 {
    void f() { std::cout << "S2::f\n"; }
    void g() { std::cout << "S2::g\n"; }
};

template <class T, class V = void>
struct D : private T {
    using T::f;
};

template <class T>
struct D<T, decltype(void(&T::g))> : private T {
    using T::f;
    using T::g; // need this only if T provides g() function
};

int main() {
    D<S1>().f(); // ok. Prints 'S1::f'
    D<S2>().f(); // ok. Prints 'S2::f' 
    D<S2>().g(); // ok. Prints 'S2::g'
    return 0;
}

Live Demo


Edit:

This is another approach that is more flexible, but I have no idea how does private virtual inheritance works with real use cases. Please let me know if it may cause any issue (e.g. UB).

#include <iostream>
#include <type_traits>

struct S1 {
    void f() { std::cout << "S1::f\n"; }
};

struct S2 {
    void f() { std::cout << "S2::f\n"; }
    void g() { std::cout << "S2::g\n"; }
};

struct S3 {
    void g() { std::cout << "S3::g\n"; }
};

template <class T, class = void>
struct D_f {};

template <class T>
struct D_f<T, decltype(void(&T::f))> : private virtual T {
    using T::f;
};

template <class T, class = void>
struct D_g {};

template <class T>
struct D_g<T, decltype(void(&T::g))> : private virtual T {
    using T::g;
};

template <class T>
struct D : D_f<T>, D_g<T> {
};


int main() {
    D<S1>().f();
    D<S2>().f();
    D<S2>().g();
    D<S3>().g();
    return 0;
}

Live Demo

Downward answered 30/1, 2015 at 9:36 Comment(3)
This doesn't scale well, though. For n functions that may or may not be available, you need to spell out 2**n class definitions.Corral
@BryanChen Is it possible to limit the change inside main D class template, ie, without the addition of specialization and duplication of code like using T::f; ?Dismissal
I have exactly the same problem except that my structs don't inherit from T, T is only a template variable. @BryanChen as soon as I remove the inheritance from your solution it no longer works. Do you know why, and what can be done to circumvent this?Advisory

© 2022 - 2024 — McMap. All rights reserved.