2016-08-29 3 views
4

Betrachten Sie den folgenden Code ein:Ausweichfunktion mit Auslassungspunkten: Können wir die Größe des Parameterpakets erzwingen?

#include <utility> 
#include <iostream> 

struct S { 
    template<typename T, typename... A> 
    auto f(A&&... args) -> decltype(std::declval<T>().f(std::forward<A>(args)...), void()) { 
     std::cout << "has f(int)" << std::endl; 
    } 

    template<typename> 
    void f(...) { 
     std::cout << "has not f(int)" << std::endl; 
    } 
}; 

struct T { void f(int) { } }; 
struct U { }; 

int main() { 
    S s; 
    s.f<T>(42); // -> has f(int) 
    s.f<U>(42); // -> has not f(int) 
    // oops 
    s.f<T>(); // -> has not f(int) 
} 

Da der dritte Aufruf f Arbeiten im gezeigten Beispiel ganz gut, auch wenn die Anzahl der Argumente ist falsch, denn es ist überhaupt für die Ausweichfunktion nicht falsch ist.

Gibt es eine Möglichkeit, die Anzahl der Argumente zu erzwingen, wenn eine Ellipse auf diese Weise beteiligt ist?
Ich meine, kann ich zur Kompilierzeit überprüfen, dass die Größe der Argumente Liste genau 1 ist, egal ob die Hauptfunktion oder der Fallback gewählt wird?

Gute Lösungen sind auch diejenigen, die nur die erste Template-Funktion beinhalten und wegen der Größe des Parameter-Packs zu Hard-Fehlern anstatt zu Soft-Fehlern führen.


Natürlich kann es mit mehreren Techniken gelöst werden, ohne variadic Argumente zu verwenden. Als Beispiel: int/char Dispatching auf interne Template-Methoden; explizit die Liste der Argumente angeben; was auch immer ...
Die Frage ist nicht über alternative Ansätze, das zu tun, ich kenne sie bereits.
Es ist nur zu wissen, ob ich hier etwas Grundlegendes vermisse oder es ist nicht möglich und das ist alles.

+0

... warum genau brauchen Sie das? Man sollte meinen, dass das Erzwingen von Endbenutzern, Dummy-Argumente zu übergeben, ein schreckliches API-Design ist. –

+0

@ T.C. Meinetwegen. Ich habe etwas mit einer Kollegin versucht, und Zweifel entstanden. Nur neugierig, es ist nicht für Produktionssoftware gedacht. Das eigentliche Problem ist größer und nicht geeignet für ein besseres Beispiel, um Post auf SO zu sein. – skypjack

+0

'Gibt es eine Möglichkeit, die Anzahl der Argumente zu erzwingen, wenn eine Ellipse auf diese Weise beteiligt ist?': Nicht wirklich, nein. Variadische Argumente haben nur die Operationen 'va_start',' va_end' und 'va_list'. – AndyG

Antwort

1

Wenn ich das richtig das Problem zu verstehen, können Sie eine Schicht hinzufügen:

struct S { 
private: 
    template<typename T, typename... A> 
    auto f_impl(A&&... args) 
    -> decltype(std::declval<T>().f(std::forward<A>(args)...), void()) { 
     std::cout << "has f(int)" << std::endl; 
    } 

    template<typename> 
    void f_impl(...) { 
     std::cout << "has not f(int)" << std::endl; 
    } 
public: 

    template<typename T, typename A> 
    auto f(A&& args) { return f_impl<T>(std::forward<A>(arg)); } 
}; 

Mit Züge, können Sie

template <typename T, typename ... Ts> 
using f_t = decltype(std::declval<T>().f(std::declval<Ts>()...)); 

template <typename T, typename ... Ts> 
using has_f = is_detected<f_t, T, Ts...>; 

struct S { 
    template<typename T, typename... A> 
    std::enable_if_t<has_f<T, A&&...>::value && sizeof...(A) == 1> f(A&&... args) 
    { 
     std::cout << "has f(int)" << std::endl; 
    } 

    template<typename T, typename... A> 
    std::enable_if_t<!has_f<T, A&&...>::value && sizeof...(A) == 1> f(A&&... args) { 
     std::cout << "has not f(int)" << std::endl; 
    } 
}; 

Demo

+0

... und überprüfen Sie in dieser Ebene die Größe des Parameters pack, rechts. Es funktioniert, aber ich frage mich, ob es eine Lösung gibt, für die ich keine neue Funktion definieren muss. – skypjack

+0

Hier, zur Zeit, habe ich die Argumentanzahl direkt in f festgelegt. Wenn Sie ein Merkmal erstellen, haben Sie möglicherweise zwei variadische Methoden und überprüfen die Größe mit SFINAE. – Jarod42

+0

Nicht genau was ich gesucht habe, aber es kann funktionieren. Danke. – skypjack

0

tun können Sie eine Funktion verwenden (Assertion) das bekommt Zeiger auf eine Funktion, um Größe der Parameter abzuleiten:

#include <utility> 
#include <iostream> 
template <typename...Args> 
struct size_assert{ 
    template <typename T,typename R,typename... Params> 
    constexpr static bool assert(R(T::*)(Params...)) 
    { 
     static_assert(sizeof...(Args) == sizeof...(Params),"Incorrect size of arguments!"); 
     return true; 
    } 
}; 

struct S { 

    template<typename T, typename... A, bool = size_assert<A...>::assert(&T::f)> 
    auto f(A&&... args) -> decltype(std::declval<T>().f(std::forward<A>(args)...), void()) 

    { 
     std::cout << "has f(int)" << std::endl; 
    } 

    template<typename> 
    void f(...) { 
     std::cout << "has not f(int)" << std::endl; 
    } 
}; 


struct T { void f(int) { } }; 
struct U { }; 

int main() { 
    // std::cout <<fc(&f); 
    S s; 
    s.f<T>(42); // -> has f(int) 
    s.f<U>(42); // -> has not f(int) 
    // oops 
    s.f<T>(); // -> has not f(int) 
} 
Verwandte Themen