2012-11-28 1 views
12

Questo codice ...C++ 11 segnaposto con spinta

int main() 
{ 
    using namespace std::placeholders; 
    ClassA a; 
    ClassB b, b2; 
    a.SigA.connect(std::bind(&ClassB::PrintFoo, &b)); 
    a.SigB.connect(std::bind(&ClassB::PrintInt, b, _1)); 
    a.SigB.connect(std::bind(&ClassB::PrintInt, &b2, _1)); 

    a.SigA(); 
    a.SigB(4); 
} 

dà l'errore di compilazione, "Errore: riferimento a '_1' è ambiguo"

Può essere fissato qualificando pienamente il segnaposto ...

int main() 
{ 
    // using namespace std::placeholders; 
    ClassA a; 
    ClassB b, b2; 
    a.SigA.connect(std::bind(&ClassB::PrintFoo, &b)); 
    a.SigB.connect(std::bind(&ClassB::PrintInt, b, std::placeholders::_1)); 
    a.SigB.connect(std::bind(&ClassB::PrintInt, &b2, std::placeholders::_1)); 

    a.SigA(); 
    a.SigB(4); 
} 

... ma perché il primo frammento di codice non funziona?

EDIT

solo per evitare ogni ambiguità, io sono la compilazione con Clang e Boost 1.52 con --stdlib=libc++ -std=c++0x e l'intero blocco di codice è questo ...

#include <boost/signals2.hpp> 
#include <iostream> 

struct ClassA 
{ 
    boost::signals2::signal<void()> SigA; 
    boost::signals2::signal<void (int)> SigB; 
}; 

struct ClassB 
{ 
    void PrintFoo()  { std::cout << "Foo" << std::endl; } 
    void PrintInt(int i) { std::cout << "Bar: " << i << std::endl; } 
}; 

int main() 
{ 
    // using namespace std::placeholders; 
    ClassA a; 
    ClassB b, b2; 
    a.SigA.connect(std::bind(&ClassB::PrintFoo, &b)); 
    a.SigB.connect(std::bind(&ClassB::PrintInt, b, std::placeholders::_1)); 
    a.SigB.connect(std::bind(&ClassB::PrintInt, &b2, std::placeholders::_1)); 

    a.SigA(); 
    a.SigB(4); 
} 
+0

Cosa ... compilatore stai usando? Modifica: non importa, 'clang' usa' __1' come la sua strana cosa dello spazio dei nomi. – Xymostech

+1

Se c'è qualcos'altro chiamato _1 in ambito, allora si otterrà questo errore. [EDIT] un po 'troppo tardi! – Slicedpan

risposta

36

Vediamo come il lavoro include:

#include <boost/signals2.hpp> include #include <boost/signals2/signal.hpp> che comprende #include <boost/signals2/slot.hpp> che comprende #include <boost/bind.hpp> che comprende #include <boost/bind/bind.hpp> che comprende include <boost/bind/placeholders.hpp>, che utilizza static boost::arg<1> _1; * nel namespace globale, da qui l'ambiguità.

*: Tecnicamente, _1 si trova in uno spazio dei nomi senza nome, ma è visibile a causa di una direttiva using.

Una soluzione è definire quanto segue nella parte superiore del vostro file in modo che <boost/bind/placeholders.hpp> non è incluso:

#define BOOST_BIND_NO_PLACEHOLDERS 
+1

Di tutte le grandi risposte, questa risponde più direttamente alla domanda, quindi + 1/Accetta – learnvst

11

C++ vede due identificatori globali denominati _1. Non ha idea che intendi lo std::placeholders::_1 invece di _1 di Boost. Questo è uno dei motivi per cui la libreria standard li mette in uno spazio dei nomi annidato: per evitare conflitti accidentali come questo.

Se ne avete bisogno di essere più breve, basta creare un semplice alias namespace:

namespace ph = std::placeholders 

Poi è solo ph::_1.

+0

Buon suggerimento, +1 – learnvst

4

GCC fornire le seguenti informazioni riguardanti il ​​vostro errore:

.../include/c++/4.7.0/functional:864:34: \ 
    error: candidates are: const std::_Placeholder<1> std::placeholders::_1 
.../boost/1.49.0/boost/bind/placeholders.hpp:55:15: \ 
    error:     boost::arg<1> {anonymous}::_1 

Il suggerimento per il problema può essere trovato nel 2 ° errore: boost::arg<1> {anonymous}::_1

La causa è che i segnaposto di boost si trovano in un anonimo namespa ce all'interno del namespace globale.

namespace 
{ 
    boost::arg<1> _1; 
    // etc... 
} // unnamed namespace 

Dal momento che i segnaposto Boost si trovano in uno spazio dei nomi anonima e si sta importando std::placeholders nel namespace globale, ora sono entrambi disponibili in ambito globale.

Come tale, non è possibile per il compilatore sapere a quale simbolo si riferisce.

Come Nicol suggerisce, utilizzare un alias dello spazio dei nomi per creare un prefisso stenografico a std::placeholders::_1 per ridurre la digitazione.

3

Poiché si utilizza std::bind Suppongo che si disponga di un supporto per C++ 11. Prendere in considerazione anche questa versione (cioè preferiscono lambda su std::bind)

int main() 
{ 
    ClassA a; 
    ClassB b, b2; 
    a.SigA.connect([&](){ b.PrintFoo(); }); 
    a.SigB.connect([&](int i){ b.PrintInt(i); }); 
    a.SigB.connect([&](int i){ b2.PrintInt(i); }); 

    a.SigA(); 
    a.SigB(4); 
} 
+0

Nizza. Bind è brutto comunque! +1 – learnvst

0

Un'altra soluzione a questo all'interno di un ambito utilizzando una variabile locale:

{ 
    auto& _1 = std::placeholders::_1; 
    auto f = std::bind(&Foo::bar, b, _1); 
    ... 
}