2017-04-03 2 views
11

Der folgende CodeC++ variadische Vorlage mit Doppel

#include <initializer_list> 
#include <vector> 

template<int ...> 
const std::vector<int>*make_from_ints(int args...) 
{ return new std::vector<int>(std::initializer_list<int>{args}); } 

kompiliert (mit GCC 6.3, auf Debian/Sid/x86-64) richtig, und ich erwarte, dass es für einen Anruf wie

auto vec = make_from_ints(1,2,3); 

einen Zeiger zu einem gewissen Vektor von ganzen Zahlen zurück, die 1, 2, 3.

wenn jedoch I int durch double ersetzen, das heißt, wenn ich die folgenden (in der gleichenhinzuzufügenDatei ...) Code:

template<double ...> 
const std::vector<double>*make_from_doubles(double args...) 
{ return new std::vector<double>(std::initializer_list<double>{args}); } 

ich einen Compiler-Fehler bekommen:

basiletemplates.cc:8:17: error: ‘double’ is not a valid type 
       for a template non-type parameter 
template<double ...> 
       ^~~ 

und ich verstehe nicht, warum. Schließlich sind sowohl int als auch double skalare numerische POD-Typen (im C++ 11-Standard vordefiniert).

Wie eine Vorlage variadische Funktion nutzen zu können, um Code zu erhalten:

auto dvec = make_from_doubles(-1.0, 2.0, 4.0); 

und einen Zeiger zu einem gewissen Vektor verdoppelt bekommen enthalten -1,0, 2,0, 4,0?

BTW, kompilieren für C++ 14 (mit g++ -Wall -std=c++14 -c basiletemplates.cc), und mit clang++ (Version 3.8.1) anstelle von g++ ändern Sie nichts.

+9

'int args ...' als 'int args analysiert wird, ... ', die Definition deiner Vorlage ist einfach falsch –

Antwort

23
template<int ...> 
const std::vector<int>*make_from_ints(int args...) 
{ return new std::vector<int>(std::initializer_list<int>{args}); } 

Das Snippet hat über eine Vielzahl von Themen:

  • eine Rückkehr const std::vector<int>* statt einer std::vector<int> und dynamische Zuordnung unnötig verwenden.

    • Auch wenn Sie die dynamische Zuordnung verwenden wollten, sollten Sie std::make_unique statt new verwenden.
  • Sie definiert make_from_ints Template-Funktion zu sein, die eine beliebige Anzahl von int Template-Parameter nimmt, aber Sie sind nicht diejenigen int s einen Namen zu geben - man kann sie nicht immer verwenden können!

  • Ihre Signatur wird tatsächlich als make_from_ints(int args, ...) geparst - dies ist eine C va_args Signatur, die nichts mit variadischen Vorlagen zu tun hat.

    • Die korrekte Syntax für ein Argumentpaket lautet type... name.

Wenn Sie eine beliebige Anzahl von Argumenten eines bestimmten Typs zu übernehmen möchten, die, der einfachste Weg ist, zu verwenden, um eine regelmäßige variadische Vorlage, die eine beliebige Menge an akzeptiert schön mit Vorlage Argument Abzug funktioniert Typen und static_assert s ihre Art (oder std::enable_if für SFINAE-Freundlichkeit verwendet). Hier ein Beispiel:

template <typename... Ts> 
auto make_from_ints(Ts... xs) 
{ 
    static_assert((std::is_same<Ts, int>::value && ...)); 
    return std::vector<int>{xs...}; 
} 

template <typename... Ts> 
auto make_from_doubles(Ts... xs) 
{ 
    static_assert((std::is_same<Ts, double>::value && ...)); 
    return std::vector<double>{xs...}; 
} 

Verbrauch:

for(auto x : make_from_ints(1,2,3,4)) std::cout << x << " "; 
std::cout << "\n"; 
for(auto x : make_from_doubles(1.0,1.5,2.0,2.5)) std::cout << x << " "; 

1 2 3 4

1 1.5 2 2.5

live example on wandbox


Bitte beachte, dass ich ein C++17 fold expression bin mit zu überprüfen, ob alle Ts... eines bestimmten Typs sind hier :

static_assert((std::is_same<Ts, int>::value && ...)); 

Wenn Sie C++ 17 Funktionen haben keinen Zugriff auf diese leicht mit so etwas wie ersetzt werden kann:

template <typename... Ts> 
constexpr auto all_true(Ts... xs) 
{ 
    for(auto x : std::initializer_list<bool>{xs...}) 
     if(!x) return false; 

    return true; 
} 

// ... 

static_assert(all_true(std::is_same<Ts, int>{}...)); 
+0

Ist das' && ... 'wirklich wörtlich? Was heißt das? –

+1

@BasileStarynkevitch: Entschuldigung dafür, das nicht zu erklären - es ist ein C++ 17-facher Ausdruck. Wird meine Antwort verbessern. –

+2

Anstelle des Faltungsausdrucks können Sie 'std :: conjection' verwenden, obwohl' std :: conjection' zugegebenermaßen ein C++ 17-Feature ist. Ich finde es jedoch einfacher zu lesen als der flange Ausdruck: 'std :: conjunction ...> :: value' – Justin

Verwandte Themen