2013-08-08 10 views
9

Dla celów edukacyjnych tworzę własną klasę szablonów wektorowych w C++. Chcę, aby móc napisać (v, w) dla iloczynu skalarnego dwóch wektorów a co za tym idzie przeciążenie operator,() następująco:C++: Domyślne inicjowanie typu całkowego w funkcji szablonu

template<class T> 
const T Vector<T>::operator,(const Vector<T>& v) const 
{ 
    assertEqualSize(v); 

    T t; 
    for(size_t i=0; i<numElements; i++) { 
     t += elements[i] * v[i]; 
    } 
    return t; 
} 

Moje pytanie brzmi: w jaki sposób właściwie zainicjowana t o wartości sensownego (np 0.0 dla Vector<double>)? Próbowałem T t();, ale wtedy g ++ mówi mi, na przykład, że "double (*)()" nie może zostać przekonwertowane na "const double" w instrukcji return i że operator+=() nie zostanie zdefiniowane dla "(double(), double)".

Dziękuję bardzo!

+1

używając '(v, w)' dla produktu kropki jest raczej dziwne. Dlaczego nie po prostu przeładować '*', tj. 'V * w' dla produktu kropki? (to właśnie używam). W 3D produkt wektorowy można zaimplementować za pomocą '^'. Oczywiście mnożenie przez skalar lub macierz można również zaimplementować przy użyciu (innego przeciążenia) '*'. – Walter

+1

Zgadzam się, że ponowne użycie operatora przecinka nie jest dobrym pomysłem. Doprowadzi to do mylącego kodu. Niestety ponowne użycie '*' również nie jest dobrym pomysłem, ponieważ nie jest jednoznaczne, ponieważ produkt krzyżowy jest oczywistym kandydatem. Zwykle zapewniam funkcje dla operacji binarnych z wykorzystaniem wektorów. – juanchopanza

Odpowiedz

9

Co trzeba jest określany wartości inicjalizacji, która ma wpływ zero inicjowanie wbudowanych typów:

T t{};  // C++11 
T t = T(); // C++03 and C++11 

Powód to nie poskutkuje

T t(); 

jest to, że jest to deklaracja funkcji bez parametrów o nazwie t, która zwraca T.

+0

To jest szablonowa wersja MVP, prawda? – Borgleader

+0

@Borgleader, MVP to więcej 'Foo foo (Bar());', ale tak. – chris

+0

@Borgleader to zdecydowanie VP, ale MVP jest jeszcze bardziej V :) – juanchopanza

0

Kod zezwala na dowolny typ T, w tym te, zdefiniowane przez użytkownika, gdy prosty wartość inicjalizacji wywołuje domyślnego konstruktora i stąd może nie zerowej inicjalizacji. Byłbym zaskoczony, gdybyś miał inne intencje niż wbudowane typy T, ale użytkownicy twojego kodu mogą mieć inne pomysły ... Powinieneś tego zabezpieczyć, np. poprzez SFINAE:

template<class T> 
struct is_complex { static const bool value = false; }; 
template<class U> 
struct is_complex<std::complex<U>> : std::is_floating_point<U> {}; 

template<class T> 
typename 
std::enable_if<std::is_floating_point<T>::value || is_complex<T>::value, 
       T>::type 
T Vector<T>::operator,(const Vector<T>& v) const 
{ 
    T t{}; // value-initialisation is now guaranteed to be zero-initialisation 
    /* ... */ 
} 

Oczywiście, ten typ SFINAE mogą być stosowane do class Vector<T> jako całości (zamiast stosowania go do wybranych metody).


Dla wyjaśnienia: jest znacznie ogranicza możliwe rodzaje T tylko 6 dopuszczalnych typów (float, double, long double i odpowiednie std::complex<> ów). Oczywiście można dopuszczać typy liczb całkowitych, ale byłbym zaskoczony, gdybyś chciał operacji wektorowej na wektorach całkowitych.

+0

Nie ma sposobu, aby zabezpieczyć się przed typami zdefiniowanymi przez użytkownika, których domyślny konstruktor nie wykonuje zerowej inicjalizacji jego elementów. W jaki sposób twój kod gwarantuje taką zerową inicjalizację? – juanchopanza

+0

@juanchopanza Dla takich typów SFINAE wyłącza tę funkcję. Dozwolone są tylko typy zmiennoprzecinkowe lub 'std :: complex '. – Walter

+0

Chodzi o to, jak rozpoznać takie typy? Będziesz musiał znać wewnętrzne konstrukcje domyślnego konstruktora i ustalić, że nie są one zerowe, inicjując coś, co powinno być. Nie widzę, jak by to zrobiła SFINAE. – juanchopanza

Powiązane problemy