2013-04-22 17 views
12

Si prega di fare riferimento al codice qui sotto:direttiva using vs utilizzando dichiarazione di swap in C++

#include <algorithm> 

namespace N 
{ 

    template <typename T> 
    class C 
    { 
    public: 
     void SwapWith(C & c) 
     { 
      using namespace std; // (1) 
      //using std::swap; // (2) 
      swap(a, c.a); 
     } 
    private: 
     int a; 
    }; 

    template <typename T> 
    void swap(C<T> & c1, C<T> & c2) 
    { 
     c1.SwapWith(c2); 
    } 

} 

namespace std 
{ 

    template<typename T> void swap(N::C<T> & c1, N::C<T> & c2) 
    { 
     c1.SwapWith(c2); 
    } 

} 

Come scritto sopra, il codice non viene compilato in Visual Studio 2008/2010. L'errore è:

'void N::swap(N::C<T> &,N::C<T> &)' : could not deduce template argument for 'N::C<T> &' from 'int'. 

Tuttavia, se io commento (1) e togliere i commenti (2), si compilerà OK. Qual è la differenza tra using namespace std e using std::swap che spiega questo comportamento?

+4

Questo sembra essere un problema di ambito. La regola è (se non sbaglio) che utilizzerà sempre l'ambito più locale prima. Quindi userà 'N :: swap' invece di' std :: swap' anche se si 'usa namespace std' – andre

+10

Btw, il codice è mal formato e il programma ha un comportamento indefinito. Non è possibile aggiungere template di funzioni * overload * allo spazio dei nomi 'std', solo specializzazioni. –

+0

Inoltre, come funziona questo swap, per quanto posso dire che è un ciclo infinito. – andre

risposta

18

Il primo caso è una direttiva using (using namespace X), e che cosa significa è che i nomi di namespace X saranno disponibili per la ricerca regolare, nel primo spazio dei nomi comune X e l'ambito corrente. In questo caso, il primo antenato dello spazio dei nomi comune di ::N e ::std è ::, quindi la direttiva using renderà disponibile std::swap solo se la ricerca raggiunge ::.

Il problema qui è che quando la ricerca inizia si guarderà all'interno della funzione, quindi all'interno della classe, quindi all'interno di N e troverà ::N::swap lì. Poiché viene rilevato un potenziale sovraccarico, la ricerca regolare non continua nello spazio nomi esterno ::. Perché ::N::swap è una funzione che il compilatore eseguirà ADL (ricerca dipendente dall'argomento), ma l'insieme di spazi dei nomi associati per i tipi fondamentali è vuoto, quindi non porterà alcun altro sovraccarico. A questo punto, la ricerca viene completata e viene avviata la risoluzione del sovraccarico. Proverà ad abbinare l'attuale (singolo) sovraccarico con la chiamata e non riuscirà a trovare un modo di convertire da all'argomento ::N::C e si ottiene l'errore.

D'altra parte una dichiarazione di utilizzo (using std::swap) fornisce la dichiarazione dell'entità nel contesto corrente (in questo caso all'interno della funzione stessa). Ricerca troverà immediatamente std::swap e interromperà la normale ricerca con ::std::swap e la utilizzerà.

2

Nota: Ho rimosso la definizione di scambio nello spazio dei nomi std. Non è rilevante qui. Anche senza di esso il codice avrà gli stessi problemi.


Ciò è dovuto per cercare le differenze di regole tra using directive (using namespace std) e il usingdeclaration (using std::swap)

Microsoft dice

Se una variabile locale ha lo stesso nome di una variabile namespace, la variabile namespace è nascosta. È un errore avere una variabile namespace con lo stesso nome come variabile globale.

#include<iostream> 

namespace T { 
    void flunk(int) { std::cout << "T";} 
} 

namespace V { 
    void flunk(int) { std::cout << "V";} 
} 


int main() { 
    using T::flunk; // makes T::flunk local 
    // using V::flunk; // makes V::flunk local. This will be an error 
    using namespace V; // V::flunk will be hidden 
    flunk(1); 
} 

base a questo, a causa della tua

template <typename T> 
void swap(C<T> & c1, C<T> & c2) 

std::swap sarà nascosto quando si utilizza

using namespace std; 

Quindi, l'unica swap disponibili per il modello deduzione è N::swap e non funzionerà per int s perché prevede un argomento come template class.

ma non quando

using std::swap; 

In questo caso diventa equivalente a definizione locale. E può essere usato senza problemi.

+1

Il messaggio di errore postato non menziona tale ambiguità. È chiaro che sta considerando solo uno "scambio". –

+1

Finché il compilatore può vedere lo 'scambio 'in' ', il suo codice dovrebbe essere compilato. Gli altri due rientrerebbero nella categoria SFINAE e semplicemente non sarebbero considerati (nel caso in cui fossero visibili). –

+1

@JamesKanze Sì, ma secondo le regole di ricerca sopra, in primo luogo non vi è altro swap da utilizzare per SFINAE. C'è ** solo uno ** swap visibile. – stardust

8

Il motivo ovvio è che una dichiarazione di utilizzo e una utilizzando la direttiva hanno effetti diversi. Una dichiarazione using introduce immediatamente il nome nell'ambito corrente, quindi using std::swap introduce il nome nello scope locale; La ricerca si interrompe qui e l'unico simbolo che si trova è std::swap. Inoltre, ciò avviene quando il modello è definito, quindi in seguito le dichiarazioni nello spazio nomi std non vengono trovate.Nella linea seguente, l'unica swap che verrà considerato è quello definito <algorithm>, oltre a quelli aggiunta da ADL (quindi, quello nel namespace N). (Ma questo è vero con VC++? Il compilatore non implementa correttamente ricerca del nome, quindi chissà.)

Una direttiva using specifica che appariranno i nomi "come se" sono state dichiarate nel racchiude spazio dei nomi più vicino sia la direttiva sia lo spazio dei nomi nominato; nel tuo caso, spazio dei nomi globale . E in realtà non introduce i nomi; it riguarda semplicemente la ricerca del nome. Che nel caso di un simbolo dipendente (o sempre, nel caso di VC++) ha luogo nel sito di chiamata .

Per quanto riguarda il motivo per cui si dispone di questo particolare messaggio di errore: probabilmente più un problema con VC++, poiché non vi sono certamente contesti non deducibili nel codice. Ma non c'è motivo di aspettarsi che le due varianti di abbiano lo stesso comportamento, indipendentemente dal compilatore.

+1

+1. * È vero in VS? * L'implementazione è errata in quanto aggiungerà anche le dichiarazioni trovate tra la definizione del modello e il punto di istanziazione (come molte versioni di gcc e CC), ma a parte questo troverà la corretta uno in questo caso (ci sono molti altri casi in cui non lo farà :) –

+1

@ DavidRodríguez-dribeas MS implementa una versione di ricerca dei nomi che era comune _before_ C++ 98 è stato adottato. L'hanno ovviamente adattato un po 'da allora, poiché la versione originale era definita prima dei namespace. Il modo in cui "usare namespace x;" e "using x;" sono interpretati in questa versione è indovinato da chiunque. (E non sono sufficienti 20 anni perché abbiano implementato lo standard?) –

+1

@JamesKanze MSVC non implementa correttamente la ricerca del nome in due fasi, ritardando la ricerca di nomi non dipendenti nella seconda fase. Vedi per es. questa [domanda] (http://stackoverflow.com/questions/6273176/what-exactly-is-broken-with-microsoft-visual-cs-two-phase-template-instanti) Quel problema non dovrebbe entrare in gioco qui AFAICS . – TemplateRex

Problemi correlati