c++ boost enable_if



  • *Klassentemplate :p



  • Ich würde das per CRTP lösen, etwa so:

    template<typename Type, typename ValueType, unsigned DIM>
    class TPointImpl {
    };
    
    template<typename Type, typename ValueType>
    class TPointImpl<Type, ValueType, 2> {
    public:
            void setX(ValueType val) {
                    static_cast<Type*>(this)->set(0, val);
            }
    
            void setY(ValueType val) {
                    static_cast<Type*>(this)->set(1, val);
            }
    };
    
    template<typename T, unsigned DIM>
    class TPoint : public TPointImpl<TPoint<T,DIM>, T, DIM> {
    public:
            typedef T value_type;
    private:
            T data[DIM];
    public:
            void set(unsigned dim, value_type val) {
                    data[dim]=val;
            }
    
    };
    
    int main() {
            TPoint<int, 2> p;
    
            p.setX(7);
    
    }
    


  • Ich stand neulich vor genau demselben Problem, auch für eine point-Klasse 😉 Ich hab's dann in etwa so gelöst, wobei mich aber auch alternative Vorschläge interessieren würden.

    enum no_init_tag {no_init};
    
    template<class T, int Dim, class Derived>
    struct point_base : std::array<T,Dim>
    {
      .....
      point_base()
      { std::fill(this->begin(),this->end(),T(0)); }
    
      explicit point_base(no_init_tag)
      {}
    
      Derived& operator+=(Derived const& x)
      {
        blas::axpy(Dim,1,x.begin(),this->begin());
        return self();
      }
      .....
      Alles möglich andere noch
      .....
    protected:
      Derived      & self()       {return static_cast<Derived      &>(*this);}
      Derived const& self() const {return static_cast<Derived const&>(*this);}
    };
    
    template<class T, int Dim>
    struct point : point_base<T,Dim,point<T,Dim> >
    {};
    
    template<class T>
    struct point<T,2> : point_base<T,2,point<T,2> >
    {
      typedef point_base<T,2,point<T,2> > base_type;
      point()
      {}
      point(T x, T y)
      : base_type(no_init)
      { (*this)[0] = x; (*this)[1] = y; }
    
      void setX(T x){(*this)[0]=x;}
      void setY(T y){(*this)[1]=y;}
    };
    

    Also: Der gemeinsame Kram ist in point_base drin und die speziellen Konstruktoren gibt's dann in der Spezialisierung einer abgeleiteten Klasse.

    So richtig dolle gefällt mir dieser Workaround nicht. Aber was viel besseres fällt mir auch nicht ein. Mit 'nem variadischen Konstruktortemplate könnte man es ja auch regeln, allerdings unterstützt das der Compiler von Microsoft noch nicht.



  • Shade Of Mine schrieb:

    Ich würde das per CRTP lösen, etwa so:

    Danke, das sieht super aus.
    Das einzige was mich daran stört, ist, dass man den namespace mit internen Typen vollmüllt.



  • vlad_tepesch schrieb:

    Shade Of Mine schrieb:

    Ich würde das per CRTP lösen, etwa so:

    Danke, das sieht super aus.
    Das einzige was mich daran stört, ist, dass man den namespace mit internen Typen vollmüllt.

    Du kannst *Impl ja auch in einen anderen Namespace stecken.


  • Mod

    Da in C++11 auch die Templateparameter von Funktionstemplates Defaultargumente haben dürfen, kann im Prinzip jede Funktion in ein Template umgewandelt werden

    template <bool a, bool b> struct and_ : std::integral_constant<bool, a && b> {};
    
    template <typename T, size_t N>
    class point
    {
    public:
        template <typename U = void, typename std::enable_if<and_<N==2||N==3, std::is_same<U,void>::value>::value, int>::type = 0>
        const T& get_x() const { return (*this)[0]; }
        template <typename U = void, typename std::enable_if<and_<N==2||N==3, std::is_same<U,void>::value>::value, int>::type = 0>
        const T& get_y() const { return (*this)[1]; }
        template <typename U = void, typename std::enable_if<and_<N==3,       std::is_same<U,void>::value>::value, int>::type = 0>
        const T& get_z() const { return (*this)[2]; }
        const T& operator[](size_t i) const { return m_data[i]; }
    // ...
    private:
       std::array<T, N> m_data;
    };
    


  • camper schrieb:

    template <bool a, bool b> struct and_ : std::integral_constant<bool, a && b> {};
    
    template <typename T, size_t N>
    class point
    {
    public:
        template <typename U = void, typename std::enable_if<and_<N==2||N==3, std::is_same<U,void>::value>::value, int>::type = 0>
        const T& get_x() const { return (*this)[0]; }
        template <typename U = void, typename std::enable_if<and_<N==2||N==3, std::is_same<U,void>::value>::value, int>::type = 0>
        const T& get_y() const { return (*this)[1]; }
        template <typename U = void, typename std::enable_if<and_<N==3,       std::is_same<U,void>::value>::value, int>::type = 0>
        const T& get_z() const { return (*this)[2]; }
        const T& operator[](size_t i) const { return m_data[i]; }
    // ...
    private:
       std::array<T, N> m_data;
    };
    

    Muss das so kompliziert? Was spricht denn gegen

    template <typename T, size_t N>
    class point
    {
    public:
        template <class = typename std::enable_if<(N==2||N==3)>::type>
        const T& get_x() const { return (*this)[0]; }
        template <class = typename std::enable_if<(N==2||N==3)>::type>
        const T& get_y() const { return (*this)[1]; }
        template <class = typename std::enable_if<(      N==3)>::type>
        const T& get_z() const { return (*this)[2]; }
        const T& operator[](size_t i) const { return m_data[i]; }
    // ...
    private:
       std::array<T, N> m_data;
    };
    

    ?

    Edit: Gut, das schützt nicht wirklich davor, dass jemand get_x<void>() schreibt. Insofern ist das, was du gezeigt hast, "idiotensicher" -- wobei das and_ aber trotzdem überflüssug aussieht.


  • Mod


  • Mod

    krümelkacker schrieb:

    Muss das so kompliziert? Was spricht denn gegen

    template <typename T, size_t N>
    class point
    {
    public:
        template <class = typename std::enable_if<(N==2||N==3)>::type>
        const T& get_x() const { return (*this)[0]; }
        template <class = typename std::enable_if<(N==2||N==3)>::type>
        const T& get_y() const { return (*this)[1]; }
        template <class = typename std::enable_if<(      N==3)>::type>
        const T& get_z() const { return (*this)[2]; }
        const T& operator[](size_t i) const { return m_data[i]; }
    // ...
    private:
       std::array<T, N> m_data;
    };
    

    Das ist ill-formed für alle N!=3. Bei der Instantiierung von point<T,N> werden auch alle Memberdeklarationen instantiiert. Da

    typename std::enable_if<(N==2||N==3)>::type
    

    nicht von einem Templateparamter des Funktionstemplates selbst abhängt, wird der Bezeichner an dieser Stelle gebunden. Und weil es sich dabei nicht um einen Typ handelt (enable_if<false>::type existiert ja nicht), ist die Deklaration ill-formed. Und weil es hier nicht um die Zusammenstellung eines Überladungssets geht, greift SFINAE nicht.

    and_ mag in diesem Zusammenhang übertrieben sein

    template <bool b = true, typename std::enable_if<(N==2||N==3)&&b, int>::type = 0>
        const T& get_x() const { return (*this)[0]; }
    

    funktioniert wahrscheinlich auch. Ganz sicher bin ich mir aber nicht (14.6/8).

    Edit:

    template <bool enable = N==2||N==3, typename std::enable_if<enable, int>::type = 0>
        const T& get_x() const { return (*this)[0]; }
    

    ist simpel und problemlos. Ganz nebenbei kann man die Funktion auch für andere N explizit benutzen, was zumindest in diesem Fall nicht unbedingt sinnlos ist.



  • 👍
    danke für den beitrag!


Anmelden zum Antworten