2017-01-20 2 views

Antwort

11

Sie können

struct B 
{ 
    using BA = A; 
    constexpr static auto Bf = f; 
} 

diese Weise können Sie die Art zu spezifizieren keine Sorge zu tun haben, was ärgerlich sein kann.

Sie möchten keine nicht statische Variable deklarieren, andernfalls wird jede einzelne Kopie Ihres Objekts einen Funktionszeiger enthalten. Sie möchten auch nicht, dass es veränderbar ist, denn dann können Sie es neu zuweisen. Sie wollen auch nicht, dass es zur Laufzeit potentiell bestimmt wird, denn dann muss sich der Compiler in einem gegebenen Kontext beweisen, dass ein Aufruf an Bf wirklich f aufruft, oder aber Funktions-Indirektionskosten bezahlen. Die constexpr behandelt diese letzten zwei Punkte.

+0

TBH jeder anständige Optimierer kann ohne Hilfe feststellen, dass ein einfacher Funktionszeiger einmal durch einen konstanten Ausdruck initialisiert wird und nicht später geschrieben wird. Bei SSA bekommt man das ziemlich kostenlos. – MSalters

+0

@MSalter Ich sehe solche Aussagen die ganze Zeit, und sie sind fast immer falsch. Es ist sehr selten, dass der Compiler einen Funktionszeiger inline einbinden kann. https://godbolt.org/g/mBMZeF. Eine statische Variable ist im Grunde eine globale Variable, und wenn sie veränderbar ist, kann sie von überall zu jeder Zeit wechseln. Es ist also nicht legal, es ohne vollständige Programmanalyse einzubinden, was bei nicht-trivialen Programmen nicht möglich ist. Mit einer Instanzvariable kann es funktionieren, wenn die Funktion, die den Mitgliedsfunktionszeiger aufruft, bis zum Aufbau der Instanz inline ist. Das ist ein großes wenn, obwohl wenn. –

+0

Das Problem mit Globals (und Klassenstatik) ist, dass SSA praktisch unmöglich ist, aus ungefähr dem Grund, den Sie angegeben haben. SSA erfordert nicht nur die Sichtbarkeit aller Schreibvorgänge, sondern auch deren Reihenfolge. Deshalb sind Globals für Optimierer nicht einfach. Aber das ist eine Eigenschaft aller Globals und nicht spezifisch für globale Funktionszeiger. – MSalters

2

Wenn f ist nur eine Funktion, können Sie entweder ein Mitglied Funktionszeiger hinzufügen:

void (*Bf)(int) = f; 

oder es nur in einer Funktion wickeln:

void Bf(int i) { f(i); } 

bevorzugen wohl Letzteres, wie es vermeidet, zusätzliche Mitglieder zu B hinzuzufügen.


Wenn f eine Funktionsschablone ist, können Sie nicht nur alias, würden Sie brauchen, um es zu übermitteln:

template <class... Args> 
auto Bf(Args&&... args) 
    -> decltype(::f(std::forward<Args>(args)...)) 
    noexcept(noexcept(::f(std::forward<Args>(args...)))) 
{ 
    return ::f(std::forward<Args>(args)...); 
} 

das ist ... ja.

+2

Gibt es Gründe, warum 'Auto Bf = f; 'ist keine Möglichkeit? – Rakete1111

+1

Ich denke nicht, dass dies eine gute Lösung ist, jede einzelne Instanz von 'B' wird einen Funktionszeiger haben, der seine Größe aufblähen wird. Und es kann sogar neu zugewiesen werden. Da der Benutzer die 'using'-Deklaration als Ausgangspunkt verwendet, ist es offensichtlich in Ordnung, dass er für alle Instanzen gleich und unveränderbar ist. –

+0

@Rakete Weil es schlecht geformt ist. – Barry

2

Nir Friedman's answer ist großartig, aber es wird nicht funktionieren, falls f überlastet ist.

In C++ 14 können Sie eine Lösung für diese mit a generic lambda haben:

struct B { 
    using BA = A; 
    static constexpr auto BF = [](auto&&... args) { 
    return f(std::forward<decltype(args)>(args)...); 
    }; 
}; 

Wenn das ein complicat Bit scheint, die nur wegen std::forward ‚s Ausführlichkeit ist. Ich werde es zeigen, ohne std::forward, um es einfacher zu machen zu erreichen (dies aber nicht verwenden, da es würde scheitern, wenn f Referenztypen als params hat):

struct B { 
    using BA = A; 
    static constexpr auto BF = [](auto... args) { return f(args...); }; 
}; 
0

Der Unterschied besteht darin, dass A ist nur eine Art, während f ist eine tatsächliche Funktion. Wenn Sie using verwenden definieren Bf als Typ von f wollen, dann verwenden:

using Bf = decltype(f); 
+0

Ich würde sagen, das ist ein Kommentar. – HolyBlackCat

Verwandte Themen