2016-01-07 20 views
7

Ho un codice Qt che ho scaricato dal mio repository SVN. È da un po 'che ci ho lavorato ma sono sicuro che è stato usato per compilare.Passa rvalue come riferimento

Ho una nuova versione di Qt e del compilatore (rispetto a quello che avevo nell'ultima volta). Il mio attuale compilatore è: mingw 4.9.2 a 32 bit.

ecco il mio codice problema:

QByteArray dataBlock = audioTestFile.read(PACKET_SIZE_TO_ENCODE); 
// This line is the issue 
uint8Vect_t testVect = encodeData(uint8Vect_t(dataBlock.begin(), dataBlock.end())); 

Dove:

typedef std::vector<uint8_t> uint8Vect_t; 

e

uint8Vect_t encodeData(uint8Vect_t &dataBuff); 

Così si può vedere qui che ho una funzione encodeData(), che prende un parametro uint8Vect_t & (superato da ref). Sto passando una variabile temporanea (un rvalore credo) creata usando il costruttore std :: vector (uno dei quali prende due iteratori) dagli iteratori QByteArray dataBlock (che ho testato funziona).

Tuttavia, ottengo l'errore:

../audioTest/txaudiostream.cpp: In member function 'void CTxAudioStream::playFile()': ../audioTest/txaudiostream.cpp:212:94: error: no matching function for call to 'CTxAudioStream::encodeData(uint8Vect_t)' uint8Vect_t testVect = encodeData(uint8Vect_t(dataBlock.begin(), dataBlock.end())); ^../audioTest/txaudiostream.cpp:212:94: note: candidate is: ../audioTest/txaudiostream.cpp:36:13: note: uint8Vect_t CTxAudioStream::encodeData(uint8Vect_t&) uint8Vect_t CTxAudioStream::encodeData(uint8Vect_t &dataBuff) ^../audioTest/txaudiostream.cpp:36:13: note: no known conversion for argument 1 from 'uint8Vect_t {aka std::vector}' to 'uint8Vect_t& {aka std::vector&}'

Fondamentalmente si tratta di dire che non posso convertire da uint8Vect_t a uint8Vect_t &. Ma se passo una variabile di tipo uint8Vect_t nella funzione (piuttosto che il valore di ritorno della variabile contructor/temp), allora funziona bene.

Ho pensato che in C++ 11 puoi passare rvalues ​​.. ma ovviamente mi manca qualcosa qui. Qualcuno può spiegare:

  1. Perché questo è sbagliato?
  2. Che cos'è una soluzione efficiente/elegante (leggibile)?
+0

Qual era il tuo vecchio compilatore? MSVS? – NathanOliver

+2

Non è possibile passare un valore temporaneo (valore rvalue) per riferimento non const. Passa per valore o passa per riferimento const in base alla semantica che ti aspetti (proprietà, mutabilità, ecc.). – CoryKramer

+0

@NathanOliver C'è una buona possibilità che fosse, ero in giro con un sacco di compilatori diversi ... ma non posso essere sicuro al 100% ... ci sarebbe una differenza? la sua sintassi è corretta o no? (non essere pedante, è una domanda seria) –

risposta

11

Il tuo problema è

uint8Vect_t encodeData(uint8Vect_t &dataBuff); 

Qui si sta assumendo un riferimento a un uint8Vect_t. Funziona bene con le variabili normali ma uint8Vect_t(dataBlock.begin(), dataBlock.end()) è un oggetto temporaneo e non può essere associato al riferimento lvalue.

Se encodeData() non cambia dataBuff, la soluzione più semplice è prendere uno const & che può essere associato a un temproario.

uint8Vect_t encodeData(const uint8Vect_t &dataBuff); 

Se si deve modificare il contenuto di dataBuff allora si sarebbe dovuto scrivere un'altra versione di encodeData() che prende un riferimento rvalue

uint8Vect_t encodeData(uint8Vect_t &&dataBuff); 

Ciò consentirà al vettore temporanea di essere spostati in funzione e quindi puoi lavorarci nella funzione come faresti con un vettore normale

Credo che il motivo per cui stai vedendo questo è che il tuo vecchio compilatore era una versione di Microsoft Visual Studio. MSVS ha un'estensione non standard attivata per impostazione predefinita che consente agli oggetti temporanei di collegarsi a un riferimento lvalue. Puoi leggere ulteriori informazioni a questo indirizzo: Non-const reference bound to temporary, Visual Studio bug?

Aggiungendo questo per mostrare come è possibile modificare encodeData() per prendere un riferimento di rvalue senza dover scrivere una nuova funzione.

#include <iostream> 
#include <vector> 

std::vector<int> modify(std::vector<int>& foo) 
{ 
    for (auto & e : foo) 
     e *= 2; 
    return foo; 
} 

std::vector<int> modify(std::vector<int>&& foo) 
{ 
    return modify(foo); 
} 


int main() 
{ 
    std::vector<int> foo = modify({ 1,2,3,4,5 }); 
    for (const auto & e : foo) 
     std::cout << e << " "; 
} 

Live Example

Nell'esempio precedente modify({ 1,2,3,4,5 }) chiamate modify(std::vector<int>&& foo) e poi nella funzione foo non è un lvaue. Quindi restituiamo il risultato del passaggio del "nuovo" valore da modificare (std :: vector & foo) che restituisce un vettore modificato.

+1

Grazie per l'ottima spiegazione, +1 ... pensavo già che il movimento semantico fosse in gioco a causa di C++ 11 ... ma avevo completamente dimenticato anche la nuova sintassi ... grazie v molto –

+0

Grazie per le informazioni su MSVC ... sembra avere un senso perché sono sicuro che ho lasciato questo codice in ordine (più la sua parte piuttosto fondamentale del mio codice quindi funzionava presto) ... ma come esattamente ha funzionato è strano perché modo i valori: o .. deve essere solo in parte fortuna ... mai una cosa buona da usare nel codice che sento;) La risposta va a te per rispondere a tutte le parti della domanda e anche per la cosa MSVC ... –

+1

@code_fodder Grazie per quello. Ho anche aggiunto un esempio di come potresti sovraccaricare 'encode()' per fare un rvalue senza dover riscrivere un gruppo di codice. – NathanOliver

2

Cosa vuoi fare con/con l'oggetto che stai passando?

Quando lo si assume come , ciò significa che si desidera apportare modifiche durature ad esso, il che non ha senso se è temporaneo.

Quando lo si assume come uint8Vect_t const&dataBuff che dovrebbe significare che si desidera copiare da esso e non modificarlo, che è probabilmente quello che si desidera.

quando si prende come uint8Vect_t dataBuff che dovrebbe significare è necessario il proprio copia temporanea locale di esso, da utilizzare come si desidera e poi buttare via, e che dovrebbe essere abbastanza importante da valere il costo della copia.

Quando lo si assume come uint8Vect_t &&dataBuff ciò dovrebbe significare che si desidera apportare modifiche non durature (come il furto di contenuti) da un oggetto temporaneo che il chiamante promette in modo efficace di eliminare dopo averlo fatto.

Quest'ultima opzione è l'ultima in C++ 11 per il passaggio dei valori.

+0

Grazie per la risposta dettagliata, +1. Sto modificando i dati (pensavo di no, ma dopo una seconda occhiata sono) ... quindi hai ragione, const non funzionerà per me, devo usare la mossa semantica:) –

6

Quando si utilizza

encodeData(uint8Vect_t(dataBlock.begin(), dataBlock.end())) 

il vettore si passa nella funzione è un oggetto temporanea, e riferimenti non può legarsi a oggetti temporanei.

La semplice soluzione, se la funzione non modifica l'argomento, è quello di rendere un riferimento a un oggetto costante:

uint8Vect_t encodeData(uint8Vect_t const& dataBuff); 

Riferimenti a oggetti costanti possono legarsi a oggetti temporanei.

+0

Ok, penso Sto ottenendo questo ora (dalla tua risposta e altri commenti) ... la cosa const sembra ovvio ora (dopo la spiegazione!), +1 –

+1

Appena ricontrollato .... Sto modificando il dataBuf, quindi ho bisogno di passare con una mossa o una copia, penso. –

+1

@code_fodder Quindi ci sono fondamentalmente due soluzioni: creare una variabile temporanea e passarla alla funzione, oppure creare due overload della funzione, uno che fa riferimento a un oggetto non costante (che può modificare l'argomento passato) e uno che prende un riferimento a un oggetto costante (che non può modificare l'argomento). –

Problemi correlati