Templace Ambiguity auflösen



  • Hallo,

    ich habe folgenden Code:

    // Compares two Eigen::MatrixBase for equality up to tolerance
    template <typename DerivedA, typename DerivedB>
    constexpr bool equals (const Eigen::MatrixBase<DerivedA>& A,
                           const Eigen::MatrixBase<DerivedB>& B,
                           double tolerance = NUMERICAL_ZERO_DIFFERENCE)
    {
      return A.isApprox(B, tolerance);
    }
    
    // Compares two scalar types for equality up to tolerance
    template<class A, class B>
    constexpr bool equals(const A a, const B b, const double tolerance = NUMERICAL_ZERO_DIFFERENCE)
    {
      return boost::math::relative_difference(a, b) <= tolerance;
    }
    

    Nun habe ich das Problem, dass der Compiler auch für Objekte, die von Eigen::MatrixBase abgeleitet sind, dass untere Template verwendet.

    Eine Lösung ist:

    bool equals(const double a, const double b, const double tolerance = NUMERICAL_ZERO_DIFFERENCE)
    {
      return boost::math::relative_difference(a, b) <= tolerance;
    }
    

    damit geht mir aber die Spezialisierung weg, die ich schon gerne behalten würde.

    Wie kann ich den Compiler dazu überreden, dass erste Template zu verwenden und nur, wenn von der spezialisierten Funktionssignatur nicht passt, das untere zu verwenden?

    Danke!



  • Hey,
    generell gäbe es ja std::is_base_of , womit sich die Sache ziemlich einfach gestaltet. Da hier die Basisklasse ein Klassentemplate ist, müssen wir eine eigene Version davon schreiben. Hier eine Möglichkeit:

    namespace detail
    {
        template<typename T>
        class is_derived_from_matrix_base_helper
        {
            static char (&test(...))[1];
            template<typename U>    
            static char (&test(Eigen::MatrixBase<U>*))[2];
    
        public:
            using type = std::integral_constant<bool, sizeof(test(std::declval<T*>())) - 1>;
        };
    }
    template<typename T>
    struct is_derived_from_matrix_base : detail::is_derived_from_matrix_base_helper<T>::type
    {
    };
    template<typename T>
    constexpr bool is_derived_from_matrix_base_v = is_derived_from_matrix_base<T>::value;
    

    Nun lässt sich die allgemeine Spezialisierung von equals mit SFINAE wie folgt formulieren:

    template<class A, class B>
    constexpr std::enable_if_t<!(is_derived_from_matrix_base_v<A> && is_derived_from_matrix_base_v<B>), bool>
    	equals(A const& a, B const& b, double tolerance = NUMERICAL_ZERO_DIFFERENCE)
    {
      return boost::math::relative_difference(a, b) <= tolerance;
    }
    

    So nebenbei: top-level CV-qualifiers bei Parametern haben keine Auswirkung auf die Signatur, kann man daher getrost weglassen.

    Gruss



  • Fytch schrieb:

    So nebenbei: top-level CV-qualifiers bei Parametern haben keine Auswirkung auf die Signatur, kann man daher getrost weglassen.

    Schon, aber in der Definition machen sie Sinn, da Parameter auch nur Variablen sind.


Anmelden zum Antworten