Verwenden Sie verschiedene Funktionssätze basierend auf Template -Parametern (C ++ Merkmale?)

StackOverflow https://stackoverflow.com/questions/3431704

  •  26-09-2019
  •  | 
  •  

Frage

Ich habe eine Klasse in C ++ definiert, die eine Reihe von Skalaren vom Typ enthält T für das ich Operatoren wie Sünde, COS usw. definieren möchte, um die Bedeutung von zu definieren sin angewendet auf ein Objekt dieser Klasse, muss ich die Bedeutung von wissen sin auf den einzelnen Skalartyp angewendet T. Dies bedeutet, dass ich entsprechende mathematische Bibliotheken verwenden muss (entsprechend dem Skalartyp T) innerhalb der Klasse. Hier ist der Code wie jetzt:

template<class T>
class MyType<T>
{
    private:
        std::vector<T> list;

    // ...

        template<class U> friend const UTP<U> sin(const UTP<U>& a);
        template<class U> friend const UTP<U> cos(const UTP<U>& a);
        template<class U> friend const UTP<U> tan(const UTP<U>& a);

    //...
};

template<class T> const UTP<T> sin(const UTP<T>& a)
{
   // use the sin(..) appropriate for type T here 
   // if T were double I want to use double std::sin(double)
   // if T were BigNum I want to use BigNum somelib::bigtype::sin(BigNum)
}

Derzeit habe ich Code, der die entsprechende Mathematikbibliothek (unter Verwendung des Namespace STD;) enthüllt und dann verwenden ::sin(a) In der Sin -Funktion für meine Klasse MyType. Während dies funktioniert, scheint es ein großer Hack zu sein.

Ich sehe, dass C ++ - Merkmale verwendet werden können, um Instanzspezifische Informationen zu speichern (z. T ist double, Wenn T ist BigNum, etc..)

Ich möchte so etwas tun: (Ich weiß, dass dies nicht kompiliert wird, aber ich hoffe, das vermittelt das, was ich tun möchte)

template<T>
struct MyType_traits {
};

template<>
struct MyType_traits<double> {
    namespace math = std;
};

template<>
struct MyType_traits<BigNum> {
    namespace math = somelib::bigtype;
};

und dann in meiner MyType -Klasse neu definieren als:

template<T, traits = MyType_traits<T> >
class MyType
{
// ...
}

und dann verwenden traits::math::sin in meiner Freund Funktion. Gibt es eine Möglichkeit, wie ich den richtigen Namespace erhalten kann (parametrisiert von T) die Mathematikfunktionen enthalten?

War es hilfreich?

Lösung

Ist Argument-abhängige Look-up nicht gut genug?

#include <cmath>
#include <iostream>

namespace xxx {
class X
{
};

X sin(X) { return X(); }
} //xxx

std::ostream& operator<< (std::ostream& os, xxx::X)
{
    return os << "X";
}

template <class T>
void use_sin(T t)
{
    using std::sin; //primitive types are not in a namespace,
                    //and with some implementation sin(double) etc might not be available
                    //in global namespace
    std::cout << sin(t) << '\n';
}

int main()
{
    use_sin(1.0);
    use_sin(xxx::X());
}

Dies würde für x funktionieren, weil sin(X) wird im gleichen Namespace als X definiert. Wenn Sie dies nicht tun, wird dies wahrscheinlich nicht helfen ...

Andere Tipps

Es ist nicht die spezifische Antwort, nach der Sie suchen, aber wäre die Verwendung der Vorlagenspezialisierung nicht eine einfachere Option?

Wie in...

template <typename T> T sin(T& t)
{
    // does nothing
}

template <> float sin(float& t)
{
    ...
}

template <> double sin(double& t)
{
    ...
}

Usw?

Ich füge diese Antwort hinzu, weil ich es endlich geschafft habe, das zu bekommen, was ich will (mit Hilfe von sehr schönen Leuten auf ## C ++ unter irc.freenode.net). Diese Methode ermöglicht es sowohl ADL als auch statischen Orten (xxx :: math), um nach der Definition der Mathematikfunktionen zu suchen.

Auf diese Weise, wenn der Typ Parameter t des Klassentests so ist, dass:

  1. Wenn T Mathematikfunktionen als Mitglieder definiert, können wir ADL verwenden und den Namespace XXX :: Math nicht berühren (hinzufügen).
  2. Wenn T mathematische Funktionen nicht als Mitglieder definiert, sondern Funktionen aus einem bestimmten Namespace verwendet, können wir den Namespace xxx :: Math wie im folgenden Beispiel hinzufügen.

Bibliothek sieht aus wie das:

#include <vector>
#include <cmath>

namespace xxx {

// include the usual math
namespace math {
    using std::asin;
}

template <class T>
class Test
{
    std::vector<T> array;

    public:
    Test(const typename std::vector<T>::size_type length)
    {
        assert(length >= 1);
        array.assign(length, T(0.0));
    }
    friend std::ostream& operator<<(std::ostream& out, const Test<T>& a)
    {
        out << "(";
        std::copy(a.array.begin(), a.array.end(), std::ostream_iterator<T>(out, ", "));
        out << "\b\b)";
        return out;
    }

    template<class U> friend const Test<U> asin(const Test<U>& a);
};

template<class U> const Test<U> asin(const Test<U>& a)
{
    using math::asin;

    Test<U> ret(a.array.size());
    for (typename std::vector<U>::size_type i = 0; i < a.array.size(); ++i) 
        ret.array[i] = asin(a.array[i]);

    // note how we use have a using math::asin; and then a call to asin(..) here,
    // instead of a math::asin(..). This allows for ADL.

    return ret;
}

} // xxx

Klient sieht aus wie das:

#include <iostream>
#include <boost/math/complex.hpp>

// client, with some foresight, includes complex math
namespace xxx { namespace math {
    using boost::math::asin;
} }

#include "test.h"

// demo
int main(int argc, char **argv)
{
    using std::cout; using std::endl;

    xxx::Test<double> atest(3);
    cout << "atest: " <<  atest << endl;
    cout << "asin(atest): " <<  asin(atest) << endl;
    cout << endl;

    xxx::Test<std::complex<double> > btest(3);
    cout << "btest: " <<  btest << endl;
    cout << "asin(btest): " <<  asin(btest) << endl;
    cout << endl;

    return 0;
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top