2012-05-13 13 views
5

Używam następujące struct jako wkład do generate_n algorytm STL za:funktor nie są przechowywane pomiędzy kolejnymi połączeniami do generate_n

struct GenerateNumber {  
    GenerateNumber() : i(0) {} 
    int operator() (void) {   
     return i++; 
    } 
private: 
    int i; 
}; 

Przykładem z kodem, który wykorzystuje ten funktor jest:

std::vector <int> v1 (3); 
std::vector <int> v2 (3); 
GenerateNumber generateNumber; 
std::generate_n (v1.begin(), 3, generateNumber); 
std::generate_n (v2.begin(), 3, generateNumber); 

Jednak wynikiem jest, że zarówno v1, jak i v2 zawierają {0,1,2}, zamiast v2, aby zawierać {3,4,5}. Sprawdziłem z przerwą, że konstruktor GenerateNumber jest wywoływany tylko raz (wiem, że nie ma sensu, aby konstruktor był wywoływany więcej niż jeden raz, ale mimo to to sprawdziłem).

Wiem, że mogę rozwiązać ten problem, sprawiając, że i jest statyczny, ale nie rozumiem tego zachowania. Dlaczego wartość i nie jest przechowywana między kolejnymi połączeniami?

Odpowiedz

8

Obiekt generatora zostanie skopiowany po przekazaniu go do generate_n. Spróbuj użyć std::ref, tj

std::generate_n(v1.begin(), 3, std::ref(generateNumber)); 
std::generate_n(v2.begin(), 3, std::ref(generateNumber)); 

Edit: Uwaga że std::ref jest dostępna tylko w C++ 11. Został wprowadzony w TR1 jako std::tr1::ref i jest również dostępny jako boost pod numerem boost::ref.

+0

You powinien wspomnieć, że 'std :: ref' to C++ 11 (chociaż jest to również wspomniane w TR1). –

+0

Prawdopodobnie będę musiał trzymać się 'statycznego' rozwiązania, ponieważ potrzebuję mojego kodu do kompilacji zarówno w VS2010 (który implementuje C++ 11) i GCC 4.4 (który nie). Jednakże, tylko dla kompletności, kiedy używam twojej sugestii, pojawia się następujący błąd kompilacji: 'error C2679: binary '=': nie znaleziono operatora, który bierze prawy operand typu 'void' (lub nie ma akceptowalnej konwersji) \t C: \ Program Files (x86) \ Microsoft Visual Studio 10.0 \ VC \ include \ algorithm \t 1581' –

+0

@LucDanton: Dzięki za notatkę. Czasami zapominam, że był czas przed C++ 11. – nosid

3

std::generate_n przyjmuje funktor według wartości, czyli tworzy jego kopię. Możliwe, że nie sprawdzałeś, czy wywoływany jest konstruktor kopii .

W przypadku braku std::ref, a jeśli problem jest zlokalizowane jak w przykładzie, można zmodyfikować funktor wziąć odniesienie do licznika wyznaczonym w zakresie wezwań do std::generate_n:

struct GenerateNumber {  
    GenerateNumber (int& i) : struct GenerateNumber {  
    GenerateNumber() : i(0) {} 
    int operator() (void) {   
     return i++; 
    } 
private: 
    int& i; 
}; 

int main() { 
    int counter = 0; 
    std::vector <int> v1 (3); 
    std::vector <int> v2 (3); 
    GenerateNumber generateNumber(counter); 
    std::generate_n (v1.begin(), 3, generateNumber); 
    std::generate_n (v2.begin(), 3, generateNumber); 

} 
+0

Dzięki, to jest eleganckie rozwiązanie. –