Frage Anpassen einer von einer Vorlage bereitgestellten Basisklasse


Wie würden Sie eine Methode ausfüllen, wenn eine Basisklasse diese nicht bereitstellt? Ich möchte die Basisklassenmethode wiederverwenden, wenn sie bereitgestellt wird.

Z.B.:

#include <iostream>
struct Base0 { };
struct Base1 { void m() { std::cout<<"Base1\n"; } };

template<typename T>
struct Derived : public T {
  //if T doesn't provide m, define it here, otherwise reuse the base class method 
  void m(){ /*? std::cout<<"Derived\n"; ?*/ }
};

int main(){
  Derived<Base0> d0;
  d0.m(); //should print "Derived"
  Derived<Base1> d1;
  d1.m(); //should print "Base1"
}

6
2018-02-18 20:22


Ursprung


Antworten:


Mit SFINAE können Sie das tun

template<typename T>
struct Derived : public T {
private:
  template <typename U = T>
  auto m_impl(int) -> decltype(std::declval<U&>().m()){ this->U::m(); }

  template <typename U = T>
  void m_impl(... ) { std::cout<<"Derived\n"; }

public:
  void m() { m_impl(0); }
};

Demo


8
2018-02-18 20:42



Um allgemein zu sein, sollten Sie die Funktion trotzdem unter einer anderen Signatur definieren:

template<typename T>
struct Derived : public T
{
     auto m(std::false_type) { std::cout<<"Derived\n"; }
};

Dann können Sie die Methoden verwenden, die in angegeben sind Dieser Thread um zu überprüfen, ob die Basisklasse die Funktion hat m():

template <typename...>
using void_t = void;

template <typename T, template <typename> class D, typename = void>
struct detect : std::false_type {};

template <typename T, template <typename> class D>
struct detect<T, D, void_t<D<T>>> : std::true_type {};

template <typename T>
using has_m = decltype(std::declval<T>().m());

Schließlich können Sie das als verwenden

template<typename T>
struct Derived : public T
{
     auto m(std::true_type) { return T::m(); }
     auto m(std::false_type) { std::cout<<"Derived\n"; }
     auto m() { return m(detect</* const */ T, has_m>{}); }
                                ^^^^^^^^^^
                                //if m() is const
};

DEMO


2
2018-02-18 20:35



Wie Aslay Berby schon gesagt hat, ist das wahrscheinlich nicht die Art, wie du gerne gehen würdest. Wenn Sie etwas wie Eigenschaften oder richtlinienbasiertes Design implementieren möchten, können Sie den folgenden Code verwenden. Tatsächlich werden solche Entwürfe ziemlich häufig verwendet und haben auch einen idiomatischen Wert.

#include <iostream>
using namespace std;

struct StandardTraits {void foo() {cout << "standard" << endl;}};

struct Traits1 {void foo() {cout << "traits1" << endl;}};
struct Traits2 {void foo() {cout << "traits2"<< endl;}};

template<typename T = StandardTraits>
class SomeClass
{
public:
    typedef T Traits;

    void useTraits() {traits.foo();}

private:
    Traits traits;
};

int main() {

    SomeClass<> x;
    SomeClass<Traits1> y;
    SomeClass<Traits2> z;

    x.useTraits();
    y.useTraits();
    z.useTraits();

    return 0;
}

// output:
// standard
// traits1
// traits2

Siehe auch: https://en.wikipedia.org/wiki/Policy-based_design


0
2018-02-18 20:36