2016-09-29 4 views
1

Я создаю класс шаблона, который содержит вектор числовых данных (может быть int, float, double и т. Д.). И он имеет одну операцию, которая называет std::abs(). Что-то вроде следующего кода.Шаблон функции с аргументами шаблона или typename

#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; 
} 

Это правильно работает для таких типов, как int, float, double. Я также хочу использовать этот класс для таких типов, как std::complex<double>.

Оглядевшись, я обнаружил, что я мог бы использовать аргументы шаблона Шаблон:

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; 
} 

предыдущий код не работает, как мой шаблон класса ожидает два аргумента,

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

В идеале я хотел бы что-то вроде предыдущий код. В которой функция my_abs() возвращает тип аргумента шаблона, переданный моему шаблону. Например, если я использую std::complex<double> тогда моя главная функция может выглядеть примерно так:

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; 
} 

Я не знаю, как это может быть достигнуто (или, если это вообще возможно, используя один и тот же класс шаблона).

+1

Просто специализироваться на 1-ый вариант для типа, как 'станд :: комплекса ' возможно. –

ответ

3

Вы можете использовать тип возвращаемого std::abs(T) в вашем объявлении.


Пример:

#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

Спасибо, это именно то, что я искал. – DSolis

0

Вы должны написать специализацию таким образом

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

Зачем нужна специализация? Его оригинальный шаблон работает для 'std :: complex '. – user2296177

+0

@ user2296177 - потому что OP необходимо извлечь тип 'U' (форма' double') 'T ' для построения 'MyData ' возвращаемого типа для 'my_abs()' – max66

+0

@downvoter - пожалуйста, можете ли вы объясните, что не так в моем ответе? – max66

0

Вам не нужен класс для этого, функция шаблона достаточно. Вы можете дать функцию abs функции вашего шаблона, например.

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; 
} 

называя это как

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

, и если у вас есть различные типы для ввода и вывода, вы можете параметризовать на T и U.


Как @ Jarod42 уже отмечалось, это не работает для перегруженных функций, таких как std::abs. Вы можете обойти это, предоставив лямбду в качестве второго аргумента, например.

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

Другим решением является явно выбрать подходящий abs литьем для соответствующего типа

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

Как 'std :: abs' имеет перегрузку,' my_abs (in, std :: abs); 'не будет компилироваться. – Jarod42

+0

К сожалению, вы правы. –

+0

Вы можете сделать 'my_abs (in, [] (const auto & e) {return std :: abs (e);});' – Jarod42

Смежные вопросы