2013-04-03 2 views
0

Я написал класс шаблона, который должен работать для double и std :: complex. как и предполагалось, все мои методы находятся в файле .hpp. все кроме одного. я должен был специализировать метод, потому что в каком-то месте мне приходится вычислять квадрат двойника или норму std :: complex. более явно для "двойной специализации" (A):C++ template double std :: complex <double> norm и product

double a(2.0); 
double b(0.0); 
b = a*a; 

для "комплексной специализации" (B):

std::complex<double> a(2.0,3.0); 
double b(0.0); 
b = std::norm(a); 

мои вопросы:

  • находится там способ избежать специализации диссертаций с помощью функции, которая работает как для двух, так и для сложных? (потому что std :: norm работает только для сложного ...)

  • или единственное решение состоит в том, чтобы отдать двойной а специализации (A) в комплекс, а затем использовать только специализацию (B) как общий шаблон (работает как для двух, так и для сложных)?

ответ

2

Вы можете минимизировать расходящийся случай, вводя свою собственную функцию, как квадрат/норма обертке:

template <typename T> 
double square_or_norm(T x); 

template<> 
inline double square_or_norm(double x) { return x * x; } 

template <> 
inline double square_or_norm(std::complex<double> x) { return norm(x); } 

Затем использовать его внутри функции, которая нуждается в ней:

template <typename T> 
T computation(T x) { 
    return some_code_with(x, square_or_norm(x)); 
} 
+0

спасибо я вас понимаю! но у меня все еще есть метод, который специализирован и должен быть в отдельном файле .cpp ... – PinkFloyd

+0

@ user2110463 Он не обязательно должен быть в .cpp-файле. Я отметил специализации 'inline', поэтому они могут находиться в заголовке вдоль остальных ваших функций. – Angew

+0

ОК теперь я понимаю еще одно преимущество встраивания ... thx – PinkFloyd

1

Вы можете определить два шаблона функции перегрузки (когда дело доходит до функциональных шаблонов, перегрузка is u sually предпочтительно специализация) называется compute_norm(), один принимает std::complex и один принимает неограниченные типы. Непринужденный шаблон будет вызывать operator *, в то время как шаблон с ограничениями будет вызывать std::norm().

#include <complex> 

template<typename T> 
double compute_norm(T t) 
{ return t * t; } 

template<typename T> 
double compute_norm(std::complex<T> const& t) 
{ return std::norm(t); } 

Затем ваш общий код, который может работать как с double и с complex<double> назвали бы compute_norm():

#include <iostream> 

template<typename T> 
void foo(T&& t) 
{ 
    // ... 
    double n = compute_norm(std::forward<T>(t)); 
    std::cout << n << std::endl; 
    // ... 
} 

Например, следующая программа:

int main() 
{ 
    double a(2.0); 
    foo(a); 

    std::complex<double> c(2.0, 3.0); 
    foo(c); 
} 

Выведет :

4 
13 

Вот live example.

+0

спасибо! но тогда у меня все еще есть метод, который специализирован и должен быть в отдельном файле .cpp. – PinkFloyd

0

Почему бы просто не использовать функцию перегрузки?

double myNorm(double); 
double myNorm(std::complex<double>); 


double myNorm(double x) { 
    return x * x; 
} 
double myNorm(std::complex<double> x) { 
    return std::norm(x); 
} 

Вы можете поместить реализацию в свой .cpp или (когда встроенный) в свой файл заголовка.

+0

да, но std :: norm() из std :: complex вычисляет квадрат абс ... – PinkFloyd

+0

@ user2110463 Хорошо, не знал этого. Так что это будет непротиворечиво, удалив мое предупреждение;) – leemes

+0

Я знаю, что это странно – PinkFloyd

1

Если у вас есть соответствующие требования стандартной библиотеки, есть перегрузка std::norm для типов с плавающей точкой:

26.4.9 Дополнительных перегруженными [cmplx.over]
следующие шаблоны функций должны иметь дополнительными перегрузки: arg norm conj proj imag real
дополнительные перегрузки должны быть достаточными для обеспечения:

  1. Если аргумент имеет тип long double, то он эффективно передается в сложный.
  2. В противном случае, если аргумент имеет тип double или целочисленный тип, тогда он эффективно ливается в сложный < double>.
  3. В противном случае, если аргумент имеет тип float, то он эффективно передается в сложный.

Это должно работать (и делает на gcc 4.7.2)

#include <complex> 
#include <iostream> 

int main() 
{ 
    std::complex<double> c {1.5, -2.0}; 
    double d = 2.5; 

    std::cout << "|c| = " << std::norm(c) << '\n' 
       << "|d| = " << std::norm(d) << '\n'; 
} 
+0

Конечно, но я бы хотел посмотреть на сгенерированный код. Я видел std :: norm call cabs() [это действительно hypot (re, im)] и квадрат результата inline. Возможно, компилятор пропустит эти накладные расходы, когда часть изображения равна нулю. Я подозреваю, что это использование «кабин» сделано для того, чтобы получить преимущество проверки inf/nan, которую выполняет гипотония (например, hypot (inf, nan) = inf). Или, можно надеяться, 'std :: norm (double x)' на самом деле просто '(x * x)', а не 'std :: norm (std :: complex (x, 0.0))' подразумевается стандартом , – greggo