2014-10-07 9 views
6

Ho una sensazione di intestino, VS2012 è sbagliato su questo, ma non ne sono certo.Il programma viene eseguito in Visual Studio 2012 ma non ideone.com

Dopo aver cercato this question, mi sono sentito come se volessi implementare qualcosa di simile.

La mia versione funziona correttamente su Visual Studio 2012, ma non si compila nemmeno su Ideone.

Qui è la mia interfaccia principale:

#include <iostream> 
#include <string> 

template <class In, class Out> 
struct Pipe 
{ 
    typedef In in_type ; 
    typedef Out out_type ; 

    In in_val ; 

    Pipe (const in_type &in_val = in_type()) : in_val (in_val) 
    { 
    } 

    virtual auto operator()() const -> out_type 
    { 
     return out_type() ; 
    } 
}; 

template <class In, class Out, class Out2> 
auto operator>> (const Pipe <In, Out> &lhs, Pipe <Out, Out2> &rhs) -> Pipe <Out, Out2>& 
{ 
    rhs = lhs() ; 
    return rhs ; 
} 

template <class In, class Out> 
auto operator>> (const Pipe <In, Out> &lhs, Out &rhs) -> Out& 
{ 
    rhs = lhs() ; 
    return rhs ; 
} 

Ecco alcune classi di test:

struct StringToInt : public Pipe <std::string, int> 
{ 
    StringToInt (const std::string &s = "") : Pipe <in_type, out_type> (s) 
    { 
    } 

    auto operator()() const -> out_type 
    { 
     return std::stoi (in_val) ; 
    } 
}; 

struct IntSquare : public Pipe <int, int> 
{ 
    IntSquare (int n = 0) : Pipe <in_type, out_type> (n) 
    { 
    } 

    auto operator()() const -> out_type 
    { 
     return in_val * in_val ; 
    } 
}; 

struct DivideBy42F : public Pipe <int, float> 
{ 
    DivideBy42F (int n = 0) : Pipe <in_type, out_type> (n) 
    { 
    } 

    auto operator()() const -> out_type 
    { 
     return static_cast <float> (in_val)/42.0f ; 
    } 
}; 

Ed ecco il conducente:

int main() 
{ 
    float out = 0 ; 
    StringToInt ("42") >> IntSquare() >> DivideBy42F() >> out ; 
    std::cout << out << "\n" ; 

    return 0 ; 
} 

Ideone si lamenta deduzioni template e non è in grado di trovare la corretta funzione operator>> candidato:

prog.cpp: In function ‘int main()’: 
prog.cpp:75:21: error: no match for ‘operator>>’ (operand types are ‘StringToInt’ and ‘IntSquare’) 
    StringToInt ("42") >> IntSquare() >> DivideBy42F() >> out ; 
        ^
prog.cpp:75:21: note: candidates are: 
prog.cpp:23:6: note: Pipe<Out, Out2>& operator>>(const Pipe<In, Out>&, Pipe<Out, Out2>&) [with In = std::basic_string<char>; Out = int; Out2 = int] 
auto operator>> (const Pipe <In, Out> &lhs, Pipe <Out, Out2> &rhs) -> Pipe <Out, Out2>& 
    ^
prog.cpp:23:6: note: no known conversion for argument 2 from ‘IntSquare’ to ‘Pipe<int, int>&’ 
prog.cpp:30:6: note: template<class In, class Out> Out& operator>>(const Pipe<In, Out>&, Out&) 
auto operator>> (const Pipe <In, Out> &lhs, Out &rhs) -> Out& 
    ^
prog.cpp:30:6: note: template argument deduction/substitution failed: 
prog.cpp:75:35: note: deduced conflicting types for parameter ‘Out’ (‘int’ and ‘IntSquare’) 
    StringToInt ("42") >> IntSquare() >> DivideBy42F() >> out ; 

Quale compilatore è corretto? Se Ideone è corretto, c'è qualche soluzione facile a questo codice?

+3

Correlato a (* forse duplicato? *): [Riferimento non const associato a temporaneo, bug di Visual Studio?] (Http://stackoverflow.com/q/16380966/1708801) ... in pratica gcc è corretto qui . Se costruisci con '/ Za' in Visual Studio fallirà anche, [vedi live] (http://rextester.com/GYMOJ23021). –

+3

È sciocco votare per chiudere questa domanda a causa * Perché questo codice non funziona *. Il test potrebbe essere più piccolo ma è un caso di test completo con risultati contrastanti tra i compilatori e non è sicuramente ovvio per la maggior parte di ciò che è corretto. –

risposta

2

Ideone (in realtà, GCC) è corretto qui. In Visual Studio, viene compilato a causa di un'infame estensione che consente ai vincoli di associarsi a riferimenti non costanti (lo standard lo proibisce).

vedo diversi modi possibili per risolvere questo in C++ standard:

One, non utilizzare provvisori per gli stadi della pipeline:

int main() 
{ 
    float out = 0 ; 
    StringToInt stage1("42"); 
    IntSquare stage2; 
    DivideBy24F stage3; 
    stage1 >> stage2 >> stage3 >> out ; 
    std::cout << out << "\n" ; 

    return 0 ; 
} 

Due, creare una funzione di "soggiorno" (opposto std::move), e l'uso che:

template <class T> 
T& stay(T &&x) { return x; } 

int main() 
{ 
    float out = 0 ; 
    stay(StringToInt ("42")) >> stay(IntSquare()) >> stay(DivideBy42F()) >> out ; 
    std::cout << out << "\n" ; 

    return 0 ; 
} 

Tre, forniscono un sovraccarico di operator >> prendendo un riferimento r-value:

template <class In, class Out, class Out2> 
auto operator>> (const Pipe <In, Out> &&lhs, Pipe <Out, Out2> &&rhs) -> Pipe <Out, Out2>& 
{ 
    return lhs >> rhs; // Notice that lhs and rhs are lvalues! 
} 

Naturalmente, si dovrebbero fornire anche sovraccarichi misti &, && e &&, &.

2

Il primo modello fallisce fondamentalmente perché non è possibile associare un valore provvisorio - IntSquare() - a un riferimento a valore non costante.

no known conversion for argument 2 from ‘IntSquare’ to ‘Pipe<int, int>&’ 

Questo dice che non è possibile inizializzare Pipe<int, int>& con una prvalue di tipo IntSquare. Purtroppo la categoria di valore non è esplicitamente menzionata nel messaggio di errore. Sebbene questa sia una regola standard, VC++ la ignora per facilitare (o fastidiosa) una vita quotidiana dei programmatori C++.

Il secondo modello

template <class In, class Out> 
auto operator>> (const Pipe <In, Out> &lhs, Out &rhs) -> Out& 
{ 
    rhs = lhs() ; 
    return rhs ; 
} 

fallisce perché per due diverse deduzioni di Out due tipi diversi sono stati desunti - il primo è int (per lhs) e la seconda è IntSquare.

+0

+1 Ho trovato entrambe le risposte utili, ma sono andato con l'altra. – jliv902

Problemi correlati