2012-02-01 18 views
15

Przepraszamy za małe pytanie dla początkujących. Istnieje wektor i wektor parC++ std :: transformuj wektor par-> pierwszy na nowy wektor

typedef std::vector <int> TItems; 
typedef std::vector < std::pair <int, int> > TPairs; 

Czy istnieje jakiś sposób, aby przekształcić wszystkie pierwsze pozycje w parze z innym wektorze w jednym kroku

int main() 
{ 
TItems items; 
TPairs pairs; 

pairs.push_back (std::make_pair(1,3)); 
pairs.push_back (std::make_pair(5,7)); 

std::transform(items.begin(), items.end(), items.begin(), comp (&pairs)); 

return 0; 
} 

Jak zaprojektować funktor?

class comp 
{ 
private: 
    TPairs *pairs; 

public: 
    comp (TPairs *pairs_) : pairs (pairs_) { } 

    unsigned int operator() (const unsigned int index) const 
    { 
     return (*pairs)[index].second != pairs->end(); //Bad idea 
    } 
}; 

Być może istnieje bardziej przyjazna dla użytkownika metoda bez wyrażeń lambda i pętli. Dzięki za pomoc.

Odpowiedz

3

naprawdę chcę do korzystania std::get jako funktora, bo to już przewidziane jako funkcja biblioteki !!

Czy nie byłoby wspaniale, gdybyśmy mogli napisać tę linię?

std::transform(pairs.begin(), pairs.end(), std::back_inserter(items), std::get<0>); 

... Ale to trochę bardziej przerażające. Trzeba disambiguate która get używać:

int main() { 
    std::vector<int> items; 
    std::vector<std::pair<int, int>> pairs; 

    pairs.push_back(std::make_pair(1, 3)); 
    pairs.push_back(std::make_pair(5, 7)); 

    std::transform(pairs.begin(), pairs.end(), std::back_inserter(items), 
       (const int& (*)(const std::pair<int, int>&))std::get<0>); 

    return 0; 
} 

Problemem jest std::getis overloaded wziąć 1. pair& 2. const pair& i 3. pair&& jako parametry, tak, że będzie pracować dla jakiejkolwiek pary jako wejście.Niestety, przeciążenia uzyskać w drodze potrącenia typu szablon dla std::transform, więc nasz oryginalnej linii

std::transform(pairs.begin(), pairs.end(), std::back_inserter(items), std::get<0>); 

plony

error: no matching function for call to ‘transform(std::vector<std::pair<int, int> >::iterator, std::vector<std::pair<int, int> >::iterator, std::back_insert_iterator<std::vector<int> >, <unresolved overloaded function type>)’ 
    std::transform(pairs.begin(), pairs.end(), std::back_inserter(items), std::get<0>); 
                        ^
... 

/usr/include/c++/4.8/bits/stl_algo.h:4915:5: note: template argument deduction/substitution failed: 
note: couldn't deduce template parameter ‘_UnaryOperation’ 
    std::transform(pairs.begin(), pairs.end(), std::back_inserter(items), std::get<0>); 

on nie wie, które przeciążenie std::get prosicie podczas wyciągania szablon dla std::transform, więc musisz określić go ręcznie. Przesyłanie wskaźnika funkcji do odpowiedniego typu informuje kompilator: "Hej, użyj przeciążenia, w którym get pobiera const& i zwraca const&!"

Ale przynajmniej używamy standardowych komponentów biblioteki (yay)?

I pod względem liczby linii, to nie gorzej niż inne opcje: http://ideone.com/6dfzxz

+1

Czy ktoś może pomyśleć o jakichkolwiek ulepszeniach? Byłoby wspaniale móc w prosty sposób używać 'std :: get' w ten sposób. ... Naprawdę powinienem prawdopodobnie używać 'reinterperet_cast &)> (std :: get <0>)', ale to wydaje się jeszcze gorsze ... – NHDaly

+0

Myślę, że to można zastąpić "twardą" obsadę funkcją get zawiniętą wewnątrz lambda, dla której można podać argumenty –

3

Co powiesz na to?

items.reserve(pairs.size()); 
for (size_t it = 0; it < pairs.size(); ++it) { 
    items.push_back(pairs[it].first); 
} 

Łatwo zrozumieć i debugować.

+0

@ Kotliński: Dzięki, ale jest to powszechne rozwiązanie. Chciałbym znaleźć jednoetapowe rozwiązanie bez pętli, jeśli to możliwe. – justik

+1

Poprosiłeś o coś przyjaznego dla użytkownika, a następnie wprowadzanie w błąd z pewną okrucieństwem C++ byłoby mylące :) –

+0

+1: najprostszy w tym przypadku. Po co unikać pętli, jeśli upraszczają? – stefaanv

15

Przede wszystkim powinieneś użyć back_inserter jako trzeciego argumentu dla transform, aby przekształcone wartości zostały przesunięte do tyłu wektora.

Po drugie, potrzebujesz jakiegoś funktora, który bierze parę int i zwraca pierwszy. Należy to zrobić:

int firstElement(const std::pair<int, int> &p) { 
    return p.first; 
} 

Teraz, aby umieścić kawałki razem:

TPairs pairs; 
pairs.push_back(std::make_pair(1, 3)); 
pairs.push_back(std::make_pair(5, 7)); 

TItems items; 
std::transform(pairs.begin(), pairs.end(), std::back_inserter(items), 
       firstElement); 

Po tym kodzie items zawiera 1 do 5.

+0

@ Freirich Raabe: Dzięki, to działa. – justik

+2

Czy istnieje jakiś sprytny sposób używania [std :: get <0>] (http://en.cppreference.com/w/cpp/utility/tuple/get) zamiast niestandardowej funkcji? – NHDaly

2

Jak na temat korzystania std::bind?

std::transform(pairs.begin(), 
       pairs.end(), 
       std::back_inserter(items), 
       std::bind(&TPairs::value_type::first, std::placeholders::_1)); 

(Wymień std::bind przez boost::bind dla non-kodu C++ 11)

10

zobaczyć Frerich lub odpowiedź Kotliński dla C++ 03.

C++ 11 Rozwiązanie z lambda:

std::transform(pairs.begin(), 
       pairs.end(), 
       std::back_inserter(items), 
       [](const std::pair<int, int>& p) { return p.first; }); 
+0

Ups, nie zauważyłem wymogu "no lambda", ale dlaczego, kiedy jest on prosty i część języka? – stefaanv

+0

Uważam, że nie jest to część języka C++, którego większość osób w rzeczywistości może używać (z powodu ograniczeń kompilatora lub z powodu pewnych wymagań w miejscu pracy). –