2012-02-01 10 views
15

Ci scusiamo per una piccola domanda per principianti. Ci sono vettoriale e vettore di coppieC++ std :: trasforma vettore di coppie-> primo a nuovo vettore

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

Esiste un modo per trasformare tutti i primi elementi di accoppiamento ad un altro vettore in un unico passaggio

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

Come progettare un funtore?

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

forse c'è qualche utente metodo più amichevole, senza le espressioni lambda e loop. Grazie per l'aiuto.

risposta

3

Ho molta voglia di utilizzare std::get come funtore, perché è già fornita come una funzione di libreria !!

Non sarebbe bello se potessimo scrivere questa riga !?

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

... Ma è un po 'più terribile di quello. È necessario disambiguare che get da usare:

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

Il problema è, std::getis overloaded a prendere 1. pair&, 2. const pair& e 3. pair&& come parametri, in modo che funziona per qualsiasi tipo di coppia come input.Purtroppo, i sovraccarichi ottengono nel senso del tipo di modello detrazione per std::transform, così la nostra linea originale

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

rendimenti

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

Non sa che il sovraccarico di std::get si sta chiedendo quando implementò il modello per std::transform, quindi è necessario specificarlo manualmente. Lanciare il puntatore alla funzione corretta dice al compilatore "Ehi, per favore usa il sovraccarico dove get prende un const& e restituisce un const&!"

Ma almeno stiamo utilizzando componenti di libreria standard (yay)?

E in termini di numero di linee, non è peggio rispetto alle altre opzioni: http://ideone.com/6dfzxz

+1

Qualcuno può pensare a qualche miglioramento? Sarebbe fantastico poter usare in modo pulito 'std :: get' in questo modo. ... Probabilmente dovrei usare 'reinterperet_cast &)> (std :: get <0>)', ma sembra ancora peggio ... – NHDaly

+0

Penso che è possibile sostituire il cast "hard" con la funzione get racchiusa all'interno di una lambda per la quale è possibile specificare gli argomenti –

3

Che ne dici di questo?

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

Semplice da capire e eseguire il debug.

+0

@ Kotlinski: Grazie, ma questa è una soluzione comune. Mi piacerebbe trovare una soluzione in un solo passaggio senza loop, se possibile. – justik

+1

Hai chiesto qualcosa di user friendly, quindi sarebbe fuorviante pubblicare una risposta con qualche atrocità in C++ :) –

+0

+1: il più semplice in questo caso. Perché evitare i loop se semplificano? – stefaanv

15

Prima di tutto, è necessario utilizzare uno back_inserter come terzo argomento su transform in modo che i valori trasformati vengano spostati sul retro del vettore.

In secondo luogo, è necessario un tipo di functor che prende una coppia di ints e restituisce il primo. Questo dovrebbe fare:

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

Ora, a mettere insieme i pezzi:

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

Dopo questo codice, items contiene 1 e 5.

+0

@ Freirich Raabe: Grazie, funziona. – justik

+2

Esiste un modo intelligente per utilizzare [std :: get <0>] (http://en.cppreference.com/w/cpp/utility/tuple/get) anziché la funzione personalizzata? – NHDaly

2

Come sull'utilizzo std::bind?

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

(Sostituire std::bind da boost::bind per i non-codice C++ 11)

10

vedere Frerich di o la risposta di Kotlinski per C++ 03.

C++ 11 soluzione lambda:

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

Oops, non ho notato il requisito "nessun lambda", ma perché quando è semplice e parte della lingua? – stefaanv

+0

Credo che non faccia parte del linguaggio C++ che la maggior parte delle persone qui intorno può effettivamente utilizzare (a causa delle limitazioni del compilatore o a causa di alcuni requisiti da parte del posto di lavoro). –