Erhalten von Funktionszeiger mit und ohne &
-
Hiho,
wenn ich eine einfache Funktion habe, kann ich die Adresse für einen Funktionszeiger ja einmal mit und einmal ohne dem Adressoperator erhalten
void foo() { cout << "I am foo!" << std::endl; } . . . auto f1 = foo; auto f2 = &foo;Gibt es bei den Varianten einen Unterschied? Ist beides ISO-konform?
Der Hintergrund meiner Frage ist folgender: ich habe eine Klasse, welche einen Handler als Template-Argument durch mehrere Methoden mit boost::bind "durchschleift".
Rufe ich die Methode mit "foo" als Parameter auf, bekomme ich einen Compilefehler. Mache ich dasselbe mit "&foo", geht alles wunderbar. Das hat mich verwundert, daher die Frage, ob das "&" einen Unterschied macht bei einfachen Funktionszeigern.
VG
Pellaeon
-
Es ist gleich und ISO-konform.
-
Funktionen zerfallen, wie Arrays, automatisch in Zeiger. Implizit. Jedoch macht das den Funktionsnamen nicht zu einem Zeiger.
Der Typ vom Ausdruck "foo" ist nach wie vor
void(). Natürlich kann aber ein Funktionsparameter nicht den Typvoid()haben. Daher wird bei der Deduzierung der Template-Argumente einfach ein Zeiger daraus gemacht.template<typename T1, typename T2> void f( T1, T2 ) { // Die Parameter können auch T1*, T2* sein static_assert( std::is_same<T1, T2>::value, "" ); } int main() { f( f<int, int>, &f<int, int> ); }Das hängt aber vom Funktionsparameter ab! Ist der nämlich eine Referenz, wird daraus:
template<typename T1, typename T2> void f( T1&&, T2&& ) { static_assert( std::is_same<T1, T2>::value, "" ); // Schlägt fehl; T1 ist eine Referenz auf eine Funktion, während T2 ein Zeiger ... ist. // Damit hat der erste Parameter den Typ void(&)(int&&, int&&) // während der zweite Parameter den Typ void(*&&)(int&&, int&&) hat - also eine Referenz auf den Zeiger. } int main() { f( f<int, int>, &f<int, int> ); }Ich nehme also an, dass die Funktionsparameter Referenzen sind, und daher Typen an Klassentemplates übergeben werden die nicht gespeichert werden können o.ä.
Das kannst du möglicherweise verhindern, in dem du auf die entsprechenden Template-Argumentestd::decayanwendest, welches automatisch function-to-pointer decays durchführt.
-
Arcoth schrieb:
Ich nehme also an, dass die Funktionsparameter Referenzen sind
Richtig. An sich möchte ich einen beliebigen Handler an einen boost-asio-Callback "durchschleifen". Und dort soll der Nutzer eben wie er möchte bind-Objekte oder eben auch einfache FUnktionen übergeben können.
/******************************************************************************/ /* adds a packet to the sending queue */ /******************************************************************************/ template<typename T> void putMessage(const CBasicMessage& msg, T&& errorHandler) { //we can't give msg to the lambda because the post call returns immediately before the invocation //therefore we create the packet here and copy it into the lambda, the packet itself contains a smart pointer to the buffer m_socket.get_io_service().post(boost::bind(&CMessageChannel::putPacket<T>, this, Packet(msg.getMsgType(), msg.getLength(), boost::const_pointer_cast<CBasicMessage::CByteBuffer>(msg.getByteBuffer())), errorHandler)); }Für diese Methode gibt es noch eine zweite Variante, welche um boost::bind-Objekte ein boost::protect legt.
putPacket wird innerhalb des ioServices abgearbeitet und fügt das Paket in die Warteschlange hinzu:
template<typename T> void putPacket(Packet& packet, T& errorHandler) { bool writeInProgess = !m_outgoing.empty(); m_outgoing.push_back(std::move(packet)); if (!writeInProgess) { startAsyncWrite(errorHandler); } };startAsyncWrite startet dann den Schreibvorgang und übergibt den Handler per bind mit an die Callback-Methode, welche boost asio dann aufruft:
template<typename T> void CMessageChannel::startAsyncWrite(T&& errorHandler) { //fill buffer with header information auto& packet = m_outgoing.front(); m_headerOutBuffer.putInt(packet.size, 0); m_headerOutBuffer.putShort(packet.type, 4); //write it auto& buf = m_headerOutBuffer.getBuffer(); boost::asio::async_write(m_socket, boost::asio::buffer(buf.begin(), buf.size()), boost::bind(&CMessageChannel::handleAsyncWriteHeader<T>, this, _1, _2, std::forward<T>(errorHandler)));Und dabei tritt eben das oben genannte Problem auf.
VG
Pellaeon
}