Nur bei bestimmten template-Parametern kompilieren



  • Hallo!

    Ich habe mehrere Klassen a, b, ... die alle von einer Klasse small abgeleitet sind:

    class small {};
    class a : public small {};
    class b : public small {};
    // usw
    

    Und ich habe mehrere Klassen A, B, ... die alle von einer Klasse huge abgeleitet sind:

    class huge {};
    class A : public huge {};
    class B : public huge {};
    // usw
    

    Außerdem habe ich eine template-Klasse foo, die eine Methode operator= besitzt:

    template<class T> class foo
    {
    public:
      foo<T> &operator=(foo<T> &f) {}
    };
    

    Nun soll eine Zuweisung genau dann kompilierbar sein, wenn beide template-Parameter von derselben Basisklasse abgeleitet wurden:

    int main()
    {
      foo<a> fooa1, fooa2;
      foo<b> foob1;
      foo<A> fooA1;
    
      // ok
      fooa1 = fooa2;
      foob1 = fooa2;
    
      // !ok
      fooA1 = fooa2;
    
      return 0;
    }
    

    Wie könnte ich das realisieren?

    Mfg



  • Falls es bei den beiden Basen small und huge bleibt, wäre dies ein Versuch wert:

    #include <boost/utility/enable_if.hpp>
    #include <boost/type_traits/is_base_of.hpp>
    #include <boost/mpl/logical.hpp>
    
    template<class T> class foo
    {
    public:
        template< typename U >
        typename boost::enable_if< 
            typename boost::mpl::or_< 
                typename boost::mpl::and_< typename boost::is_base_of< T, small >, typename boost::is_base_of< U, small > >,
                typename boost::mpl::and_< typename boost::is_base_of< T, huge >, typename boost::is_base_of< U, huge > >
            >, foo<T>& >::type 
            operator=( const foo<U>& f ) 
        {
            return *this;
        }
    };
    

    Leider übersetzt er dann den Ausdruck

    foob1 = fooa2;
    

    auch nicht, warum .. das habe ich noch nicht verstanden 😕
    :xmas2: Werner



  • ja, das grundsätzliche problem ist, dass man die basisklassen kennen müsste. oder der klassenhierarchie irgendwie informationen über ihre vererbungshierarchie mitgeben. aber das ist beides nicht ganz sauber.

    hier auch ein relativ hässlicher workaround von mir:

    #define CHECK_BASE(base)template <class U> \
       foo &assign_check (foo<U> const &f, base*, base*) \
       { return do_assign (f); }
    
    template<class T> class foo
    {
    public:
       foo &operator=(foo const &f) { return *this; }
    
       template <class U>
       foo &operator=(foo<U> const &f)
       {
          assign_check (f, static_cast<T*>(0), static_cast<U*>(0));
       }
    
    private:
       CHECK_BASE (small)
       CHECK_BASE (huge)
    
       template <class U>
       foo &do_assign (foo<U> const &f)
       {
          //...
          return *this;
       }
    };
    

  • Mod

    Werner Salomon schrieb:

    Leider übersetzt er dann den Ausdruck

    foob1 = fooa2;
    

    auch nicht, warum .. das habe ich noch nicht verstanden 😕
    :xmas2: Werner

    Weil du die Templateargumente vertauscht hast 😉 sind auch ein paar typenames zuviel:

    template<typename T>
    class foo
    {
        typedef typename boost::mpl::if_<
            boost::is_base_of< small, T >,
            small,
            typename boost::mpl::if_<
                boost::is_base_of< huge, T >,
                huge,
                void
            >::type
        >::type base; 
    public:
        foo();
        template<typename U>
        foo(const foo<U>&, typename boost::enable_if< boost::is_base_of< base, U > >::type* = 0);
    };
    

    Wenn man noch mehr Basisklassen hat, wäre eine extra Metafunktion sinnvoll, um das ganz nicht in verschachtelten if_s zu ersticken.



  • Vielen Dank! :xmas1:


Anmelden zum Antworten