c++sfinaeusing-declarationprivate-inheritance

'using' declaration as SFINAE


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)?


Solution

  • 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