2016-09-29 7 views
1

Ich erstelle eine Vorlage-Klasse, die einen Vektor von numerischen Daten enthält (kann Int, Float, Double, etc) sein. Und es hat eine Operation, die std::abs() auf den Daten aufruft. So etwas wie der folgende Code.Template-Funktion mit Vorlage Argumente oder Typname

#include <iostream> 
#include <complex> 
#include <vector> 


template<typename T> class MyData 
{ 
public: 
    std::vector<T> data; 
    MyData<T> my_abs() const; 

}; 


template<typename T> 
MyData<T> MyData<T>::my_abs() const 
{ 
    MyData<T> output; 
    output.data.reserve(data.size()); 
    typename std::vector<T>::const_iterator it; 

    for (it = data.begin(); it != data.end(); it++) 
    { 
     output.data.push_back(std::abs(*it)); 
    } 
    return output; 
} 


int main() 
{ 
    MyData<double> A; 
    A.data = std::vector<double>(10, -1.0); 

    MyData<double> test = A.my_abs(); 

    for (auto el : test.data) 
    { 
     std::cout << el << std::endl; 
    } 
    return 0; 
} 

Dies funktioniert korrekt für Typen wie Int, Float, Double. Ich möchte auch diese Klasse für Typen wie std::complex<double> verwenden können.

Umsah Ich fand, dass ich Argumente Vorlage Vorlage verwenden:

template<template<typename> class T, typename U> class MyData 
{ 
public: 
    std::vector<T<U>> data; 
    MyData<U> my_abs() const; 

}; 


template<template<typename> class T, typename U> 
MyData<U> MyData<T<U>>::my_abs() const 
{ 
    MyData<U> output; 
    output.data.reserve(data.size()); 
    typename std::vector<T<U>>::const_iterator it; 

    for (it = data.begin(); it != data.end(); it++) 
    { 
     output.data.push_back(std::abs(*it)); 
    } 
    return output; 
} 

Der vorherige Code nicht als meine Template-Klasse arbeiten erwartet zwei Argumente,

error: wrong number of template arguments (1, should be 2) 
MyData<U> abs() const; 
    ^

Idealerweise würde ich gerne etwas wie der vorherige Code In dem die my_abs() Funktion den Typ des Vorlagearguments zurückgibt, das an meine Vorlage übergeben wird. Z. B, wenn ich ein std::complex<double> dann meine Hauptfunktion etwas aussehen könnte:

int main() 
{ 
    MyData<std::complex<double>> A; 
    A.data = std::vector<std::complex<double>>(10, std::complex<double>(-1.0, -1.0)); 

    MyData<double> test = A.my_abs(); 

    for (auto el : test.data) 
    { 
     std::cout << el << std::endl; 
    } 
    return 0; 
} 

Ich bin nicht sicher, wie dies erreicht werden kann (oder wenn es sogar möglich, mit der gleichen Template-Klasse ist).

+1

einfach Ihre erste Version für eine Art spezialisiert wie 'std :: Komplex ' vielleicht. –

Antwort

3

Sie den Rückgabetyp von std::abs(T) in Ihrer Erklärung verwenden, um auszuwählen.


Beispiel:

#include <iostream> 
#include <complex> 
#include <vector> 
#include <cmath> 
#include <utility> 

template<typename T> class MyData 
{ 
public: 
    std::vector<T> data; 
    using abs_type = decltype(std::abs(std::declval<T>())); 
    auto my_abs() -> MyData<abs_type> const; 
}; 

template<typename T> 
auto MyData<T>::my_abs() -> MyData<abs_type> const 
{ 
    MyData<abs_type> output; 
    output.data.reserve(data.size()); 
    typename std::vector<T>::const_iterator it; 

    for (it = data.begin(); it != data.end(); it++) 
    { 
     output.data.push_back(std::abs(*it)); 
    } 
    return output; 
} 

int main() 
{ 
    MyData<std::complex<double>> A; 
    A.data = std::vector<std::complex<double>>(10, std::complex<double>(-1.0, -1.0)); 

    auto test = A.my_abs(); 

    for (auto el : test.data) 
    { 
     std::cout << el << std::endl; 
    } 
    return 0; 
} 
+0

Danke, genau das habe ich gesucht. – DSolis

0

Sie haben Ihre Spezialisierung auf diese Weise

template<template<typename> class T, typename U> 
class MyData<T<U>> // <----- note the <T<U>> 
{ 
public: 
    std::vector<T<U>> data; 
    MyData<U> my_abs() const;  
}; 
+0

Warum ist eine Spezialisierung erforderlich? Seine Vorlage funktioniert auch für 'std :: complex '. – user2296177

+0

@ user2296177 - weil das OP den 'U' Typ (den' doppelten' Teil) form 'T ' extrahieren muss, um den 'MyData ' Rückgabetyp für 'my_abs()' – max66

+0

@ downvoter - bitte, können Sie erklären, was in meiner Antwort falsch ist? – max66

0

Sie benötigen hierfür eine Klasse nicht zu schreiben, eine Template-Funktion genügt. Sie können Ihrer Vorlagenfunktion eine abs-Funktion zuweisen, z.

template<typename T, typename F> std::vector<T> my_abs(const std::vector<T> &in, F abs) { 
    std::vector<T> out; 
    for (auto &i: in) { 
     out.push_back(abs(i)); 
    } 

    return out; 
} 

dies als

std::vector<int> res = my_abs(in, special_abs); 

aufrufen und wenn Sie verschiedene Arten zur Ein- und Ausgabe haben, können Sie auf T und U parametrieren.


Wie @ Jarod42 bereits erwähnt, ist dies nicht wie std::abs für überladene Funktionen funktioniert. Sie können dies umgehen, indem Sie ein Lambda als zweites Argument angeben, z.

std::vector<int> res = my_abs(in, [](const auto& e) { return std::abs(e);}); 

ist eine andere Problemumgehung explizit die entsprechende abs durch Gießen auf die richtige Art kann

std::vector<int> res = my_abs(in, static_cast<double(*)(double)>(std::abs)); 
+1

Da 'std :: abs' eine Überladung hat, wird' my_abs (in, std :: abs); 'nicht kompiliert. – Jarod42

+0

Leider hast du recht. –

+0

Sie könnten 'my_abs (in, [] (const auto & e) {zurück std :: abs (e);});' – Jarod42

Verwandte Themen