2015-01-19 16 views
8

Ultimamente mi sono imbattuto in un problema che in qualche modo (ma solo in qualche modo) ha senso per me. Si basa sull'interpretazione della costruzione di un temporaneo come dichiarazione del singolo argomento del costruttore (!). Si prega di dare un'occhiata all'esempio minimo qui sotto.La costruzione di una chiamata di funzione temporanea viene interpretata come dichiarazione

#include <iostream> 

class Foo0{ 
public: 
    Foo0(int a){}; 
    void doStuff() {std::cout<<"maap"<<std::endl;}; 
}; 

class Foo1{ 
public: 
    Foo1(int a){}; 
    void doStuff() {std::cout<<"maap"<<std::endl;}; 
}; 

class Foo2{ 
public: 
    Foo2(int a){}; 
    void doStuff() {std::cout<<"maap"<<std::endl;}; 
}; 

class Bar{ 
public: 
    Bar(Foo0 foo0, Foo1 foo1, Foo2 foo2){}; 
}; 

int main() { 
    int x = 1; 

    Bar bar0(Foo0(x), Foo1(x), Foo2(x)); // Does not work: conflicting declaration ‘Foo1 x’ previous declaration as ‘Foo0 x’; conflicting declaration ‘Foo2 x’ previous declaration as ‘Foo0 x’ 
    Bar bar1(Foo0{x}, Foo1(x), Foo2(x)); // Works WTF 
    Bar bar2(Foo0(x), Foo1{x}, Foo2(x)); // Works WTF 
    Bar bar3(Foo0(x), Foo1(x), Foo2{x}); // Does not work: conflicting declaration ‘Foo1 x’ previous declaration as ‘Foo0 x’ 
    Bar bar4(Foo0{x}, Foo1{x}, Foo2{x}); // Works totally makes sens to me 

    x.doStuff(); //Dose not work. This makes sens to me. But in the context its curious though. 
} 

ho già letto che espressioni come:

Foo(a); 

sono interpretati (se v'è un costruttore standard) come dichiarazione di a. Questo ha senso ed è assolutamente soddisfacente, dal momento che puoi semplicemente usare le {} -bracks per rendere esplicita la costruzione. Ma quello che non capisco è:

  1. Perché c'è un problema con la costruzione di bar0? Tutti i Foo s non hanno un costruttore standard. Quindi non ha senso interpretare qualcosa come Foo0(x) come dichiarazione di x.

  2. Perché la costruzione di bar1 e bar2 funziona? Per me è ovvio che la costruzione di bar4 funziona, dal momento che utilizzo le {} -bracks per tutti i valori temporanei Foo s, quindi sono esplicito su ciò che voglio.

  3. Se è necessario utilizzare solo il {} -brackets con una sola delle Foo s per risolvere il problema ... perché la costruzione di bar3 fallire?

  4. Inoltre, x viene dichiarato prima che venga creata qualsiasi barra. Perché il compilatore non si lamenta di ciò?

L'ultima domanda è relativa alla mia ultima riga di codice di esempio. Per farla breve: cosa pensa il compilatore che voglio che faccia e dove mi manca l'apparenza di shadowing?

PS: Se è interessante, utilizzo gcc-4.9.2.
PPS: Ho provato lo stesso con il costruttore bar prendendo tre argomenti come Foo0 s. Stessa storia qui. Ma l'errore non dice nulla sulla dichiarazione in conflitto ma sulla ridefinizione di x.

+1

Proprio così, sapete, 'bar0' è la dichiarazione di una funzione che accetta tre' Foo's e restituisce un 'Foo', non è esso stesso un oggetto' Foo'. Inoltre, il fatto che la dichiarazione di 'bar3' non funzioni è un bug in qualunque compilatore stai usando. Quella linea dovrebbe funzionare come fa in [clang] (http://coliru.stacked-crooked.com/a/d5f8305eff385830). – 0x499602D2

+0

@ 0x499602D2 bugged per me in g ++ 4.8.1 –

+0

Giusto per essere corretto: 'bar0' è la dichiarazione di una funzione che accetta tre' Foo's e restituisce una 'Barra'. – sedriel

risposta

10

La regola è che se una dichiarazione ha la sintassi di una dichiarazione di funzione, allora è una; altrimenti è una dichiarazione variabile. Esempi sorprendenti di questo sono talvolta chiamati .

Bar bar0(Foo0(x), Foo1(x), Foo2(x)); // Does not work: conflicting declaration ‘Foo1 x’ previous declaration as ‘Foo0 x’; conflicting declaration ‘Foo2 x’ previous declaration as ‘Foo0 x’ 

Questa è una dichiarazione di funzione: bar0 è il nome, Bar è il tipo di ritorno, ei tipi di parametri sono Foo0, Foo1 e Foo2. I nomi dei parametri sono tutti x che è illegale - i nomi dei parametri di funzione devono essere diversi. Se si modifica xxx a xz l'errore scompare).

Bar bar1(Foo0{x}, Foo1(x), Foo2(x)); // Works WTF 
Bar bar2(Foo0(x), Foo1{x}, Foo2(x)); // Works WTF 
Bar bar4(Foo0{x}, Foo1{x}, Foo2{x}); // Works totally makes sens to me 

Queste linee e creare oggetti bar1, bar2 e bar4 di tipo Bar. Non possono essere analizzati come dichiarazioni di funzioni poiché la notazione { } non è una sintassi valida in una dichiarazione di funzione.

Pertanto, Foo0{x} ecc. Sono espressioni che forniscono gli argomenti al costruttore Bar. Foo0{x} e Foo0(x) sono modi equivalenti di dichiarare un temporaneo di tipo Foo0 con l'inizializzatore x.

Bar bar3(Foo0(x), Foo1(x), Foo2{x}); // Does not work: conflicting declaration ‘Foo1 x’ previous declaration as ‘Foo0 x’ 

Penso che questo sia un bug del compilatore; la parte Foo2{x} significa che questa riga non può essere una dichiarazione di funzione; e sembra una dichiarazione valida di una variabile bar3.

x.doStuff(); //Dose not work. This makes sens to me. But in the context its curious 

x è un int; non ha alcun metodo

+0

In che modo Bar0 è una dichiarazione di funzione legale? Cosa significa 'type (name)' in questo contesto? È solo un binding/grouping, come 'int (* foo)' - quindi 'type (name)' è lo stesso di 'type name'? A volte stavo davvero iniziando ad amare il C++. – JasonN

+0

@JasonN [vedi qui] (http://stackoverflow.com/questions/21624880/how-does-this-declaration-invoke-the-most-vexing-parse) per una buona spiegazione –

+0

Grazie. Awesomeness di parses complessi. '-Wvexing-parse' è divertente. Triste allo stesso tempo. – JasonN

2

1) Foo0 (x) viene considerato come un parametro della funzione bar0 in questo caso. Qui, non importa se ha un costruttore standard o no. Non è una dichiarazione e un'inizializzazione di una variabile locale, ma solo una dichiarazione di parametro in una dichiarazione di funzione.

2) La mia ipotesi è che questo ha qualcosa a che fare con l'analisi, ma qualcuno mi corregga se sbaglio .. Esempi bar1 e bar2 lavoro, perché il compilatore sa che bar1 e bar2 sono dichiarazioni di variabili locali (e non dichiarazioni di funzioni) non appena vede la prima occorrenza di {}. Queste prime occorrenze di {} appaiono prima che x sia dichiarato due volte come parametro della funzione.

3) La costruzione di bar3 non riesce, poiché il compilatore presuppone innanzitutto che bar3 sia una dichiarazione di funzione. La dichiarazione della funzione prende tre parametri, che sono tutti denominati x. Ovviamente, questo non è corretto.

4) La x nella dichiarazione di funzione è solo un nome per il parametro. È in un ambito diverso rispetto all'intero x che hai dichiarato prima.