2016-05-15 9 views
3

zu variadische Vorlagen und zum Zweck des Lernens zu überprüfen, ich bin neu betrachtet die folgende FunktionWie die Art der übergebenen Argumente zu variadische Funktion

template <typename T, typename... args> 
T* make_arr(args... arg) { 

    // Code to check if passed args are of the same type 

    T* arr = new T[sizeof...(arg)]{ arg... }; 
    return arr; 
} 

Ich habe zwei Fragen:

  1. I Ich möchte, dass die Funktion zu einem Template wird und ich möchte, dass die übergebenen Argumente vom selben Typ sind, also die Frage: Ist es möglich zu überprüfen, ob übergebene Argumente vom selben Typ sind?
  2. Ist es möglich, den Typ des Array-Zeigers abzuleiten, indem der Typ args... abgeleitet wird, ich meine, ohne <typename T> zu verwenden? ... Ich benutze decltype (arg), aber es hat nicht funktioniert ...

Hinweis: Bitte die Titelfrage bearbeiten, wenn es nicht angemessen ist ... Dank

+0

nicht sicher, aber haben Sie versucht, 'typeid' Funktion. – seleciii44

+0

Sie möchten, dass die Argumente identisch sind * und sich möglicherweise von "T" unterscheiden? Oder möchten Sie, dass alle denselben Typ wie "T" haben? Oder Sie erlauben es, dass sie sich voneinander unterscheiden, aber Sie müssen lediglich Ihr Array von 'T' mit Typen erstellen, die in' T' konvertiert werden können? –

Antwort

3

Der einzige Weg, ich fand, ist zu machen eine Hilfsfunktion SFINAE mit

//Basic function 
template<typename T> 
void allsame(T) {} 

//Recursive function 
template<typename T, typename T2, typename... Ts, 
typename = std::enable_if_t<std::is_same<T, T2>::value>> 
void allsame(T arg, T2 arg2, Ts... args) 
{ 
    allsame(arg2, args...); 
} 

Sie können es dann wie so nennen:

allsame(arg...); 

der Compiler wird dann einen Fehler aus, wenn die Typen nicht die sa sind mich.


für 2), könnten Sie allsame modfiy den Typ zurückzukehren. Der einzige Nachteil dieser Funktion ist, dass sie nicht funktioniert, wenn der Typ nicht standardmäßig konstruierbar ist.

template<typename T> 
T allsame(T) { return{}; } 

T allsame(T arg, T2 arg2, Ts... args) 

Dann können Sie decltype(allsame(args...)) den Typ

2

Zunächst einmal zu bekommen, müssen Sie diese beinhaltet:

#include <type_traits> 
#include <tuple> 

dann, lassen Sie uns variadische Vorlage erklären zu erkennen, ob Typen gleich oder nicht:

template <typename ... args> 
struct all_same : public std::false_type {}; 


template <typename T> 
struct all_same<T> : public std::true_type {}; 


template <typename T, typename ... args> 
struct all_same<T, T, args...> : public all_same<T, args ... > {}; 

wir jetzt static_assert wenn p erfassen können arameter Typen sind gleich:

template <typename T, typename... args> 
T* make_arr(args... arg) { 

    // Code to check if passed args are of the same type 
    static_assert(all_same<args ...>::value, "Params must have same types"); 

    T* arr = new T[sizeof...(arg)]{ arg... }; 
    return arr; 
}; 

Schließlich lassen Sie uns Rückgabetyp Ihrer Funktion als erste Art von Parameter übernehmen - wenn alle Typen gleich sind wir einen von ihnen nehmen. Wir verwenden std::tuple für dieses

template <typename... args> 
typename std::tuple_element<0, std::tuple<args...> >::type * make_arr(args... arg) { 

    // Code to check if passed args are of the same type 
    static_assert(all_same<args ...>::value, "Params must have same types"); 

    typedef typename std::tuple_element<0, std::tuple<args...> >::type T; 

    T* arr = new T[sizeof...(arg)]{ arg... }; 
    return arr; 
}; 
+0

Das funktioniert, aber ich habe den letzten Teil Ihrer Antwort nicht verstanden, was ist der Tupel Teil ... Wenn Sie es kurz vereinfachen können, wäre ich dankbar ... +1 sowieso – Laith

+1

@Leo 'std :: tuple' ist ein standard variadic Vorlage, wie 'std :: pair', aber mit beliebigen Typen Liste. Und es gibt eine 'std :: tuple_element'-Hilfsvorlage, die für den Zugriff auf ein Tupelfeld oder für den Tupelfeldtyp nach seinem Index verwendet werden kann. In unserem Fall repräsentiert 'std :: tuple ' eine Liste von Vorlagentypen, und wir nehmen den Typ bei Index 0 mit 'std :: tuple_element <...> :: type'. Soweit Vorlagenargumente dieselben Typen haben, können wir sie als Typ verwenden, um einen Zeiger zum Halten eines Arrays zu erzeugen. – user2807083

2

Beginnen Sie mit einer constexpr bool Funktion zu prüfen, ob alle booleans wahr sind. Dies ist nützlich, wenn Sie überprüfen, ob alle is_same Anrufe true sind.

constexpr bool all() { 
    return true; 
} 
template<typename ...B> 
constexpr bool all(bool b, B... bs) { 
    return b && all(bs...); 
} 

Wie auch immer, hier ist die make_arr Funktion:

template <typename... args 
, class ... 
, typename T = std::tuple_element_t<0, std::tuple<args...>> 
, typename = std::enable_if_t< all(std::is_same<T, args>{}...) > 
> 
T* make_arr(args&&... arg) { 
    static_assert(all(std::is_same<T, args>{}...) ,""); 
    T* arr = new T[sizeof...(arg)]{ std::forward<args>(arg)... }; 
    return arr; 
} 

Einige Kommentare:

  • perfekte Weiterleitung verwendet wird, && und std::forward, mögliche Kopien zu vermeiden, wenn Ihr Typ ist groß.
  • Der Typ des ersten Arguments wird extrahiert by creating a std::tuple type and using std::tuple_element<0, ..>.
  • wird eine static_assert verwendet, wobei jeder Typ mit dem ersten Typ verglichen wird (über is_same).
  • Ich denke, dass Sie möchten, dass SFINAE diese Funktion "versteckt", wenn die Typen nicht alle identisch sind. Dies wird erreicht über typename = std::enable_if_t< ..boolean-expression.. >
  • der class ... ist im Wesentlichen redundant. Der einzige Zweck besteht darin, es Entwicklern unmöglich zu machen, die Prüfungen zu betrügen, indem sie die Typen manuell spezifizieren (make_arr<int,char,size_t,bool>(..)). Vielleicht ist das aber zu konservativ - die static_assert wird sie trotzdem erwischen!
+0

Das war hilfreich, danke – Laith