2016-12-31 2 views
6

Ich muss das Produkt einer Reihe von Zahlen zur Kompilierzeit berechnen, die an eine Vorlagenstruktur übergeben wird. Es gelang mir, eine hässlich Lösung zu machen:Vorlage Metaprogrammierung: Multiplikation einer Reihe von Vorlagenargumenten

template<std::size_t n1, std::size_t ...args> 
struct mul_all 
{ 
    static constexpr std::size_t value = n1 * mul_all<args...>; 
}; 
template<> 
struct mul_all<0> 
{ 
    static constexpr std::size_t value = 1; 
}; 


Das Problem ist, dass jedes Mal, wenn ich 0 bis Template args wie so zu meinem struct

int main() 
{ 
    std::cout << mul_all<1,2,5,4,5,7,0>::value << " " 
       << mul_all<4,2,0>::value; 
    return 0; 
} 


einzuspeisen Gibt es eine Problemumgehung, um von der letzten Null gelesen zu bekommen?

Hinweis: Ich bin ein Anfänger in TMP.

+3

Genau, hier ist ein C + +14 'constexpr' Lösung, die keine Template-Rekursion verwendet: http://melpon.org/wandbox/permlink/yNbfyOhiN3hLqmpA – bogdan

+0

cool !!! gibt es irgendeine möglichkeit es mit der anderen lösung zu vergleichen? –

+0

Meinst du in Bezug auf die Kompilierzeit? Die nicht-rekursiven Lösungen sollten ungefähr gleich und besser als die klassischen Template-Rekursionen sein, da die rekursiven Lösungen mehrere Template-Instanziierungen erzeugen, die etwas kosten (in der Praxis beginnt es für relativ viele Template-Argumente von Bedeutung zu sein - viele Dutzende von ihnen). Die C++ 14-Dummy-Array-Lösung ist jedoch nur ein Workaround für das Fehlen von Faltungsausdrücken; Ich würde C++ 17 fache Ausdrücke wann immer verfügbar wählen. – bogdan

Antwort

6

In C++ 17, mit Ausdruck Falten, können Sie direkt zu tun

template<std::size_t ...args> 
struct mul_all 
{ 
    static constexpr std::size_t value = (args * ...); 
}; 

Vorher haben Sie die Teil-Spezialisierung zu tun:

template<std::size_t n1, std::size_t ...args> 
struct mul_all 
{ 
    static constexpr std::size_t value = n1 * mul_all<args...>::value; 
}; 

template<std::size_t n> 
struct mul_all<n> 
{ 
    static constexpr std::size_t value = n; 
}; 
+0

Ihr Vorschlag ist cool, aber es kompiliert leider nicht, ich kopierte und hinter dem Code, getestet mit GCC 7.0.0 Snapshot, aber es nicht bestanden. hast du absichtlich '' value' absichtlich von hier 'statisch constexpr std :: size_t value = n1 * mul_all ;' –

+0

@chedynajjar gelöscht: Tatsächlich '' value' 'fehlte im zweiten Schnipsel, reparierte. – Jarod42

5

Sie benötigen eine Spezialisierung ersetzen:

template<std::size_t n1, std::size_t ...args> 
struct mul_all 
{ 
    static constexpr std::size_t value = n1 * mul_all<args...>::value; 
}; 

template<std::size_t n> 
struct mul_all<n> 
{ 
    static constexpr std::size_t value = n; 
}; 
+0

Sie haben keine Rekursion in Ihrer Lösung und ich sehe nicht einmal Varidic Args. –

+0

@chedynajjar: Es ist ein Ersatz für Ihre vollständige Spezialisierung mit einer partiellen Spezialisierung: alles andere bleibt wie in Ihrem Ansatz. –

+0

@ DietmarKühl: Ich habe einen Fehler bei der Kompilierung: 'error: falsche Anzahl von Template-Argumenten (0, sollte mindestens 1 sein) statisch consExpr std :: size_t value = n1 * mul_all :: value;' –

3

Eine Möglichkeit für leere varargs spezialisiert ist. Für, dass Sie die Hauptvorlage benötigen nur variadische args zu sein:

// main template never used 
template<std::size_t ...args> struct mul_all 
{ 
}; 

// specialization for at least one arg 
template<std::size_t n1, std::size_t ...args> 
struct mul_all<n1, args...> 
{ 
    static constexpr std::size_t value = n1 * mul_all<args...>::value; 
}; 

// specialization for empty args 
template<> 
struct mul_all<> 
{ 
    static constexpr std::size_t value = 1; 
}; 

So, jetzt können Sie tun:

+0

Dies ist der richtige Weg zu tun, so weit wie Vorlage Rekursion geht. Sie müssen die Anzahl der Vorlagenargumente nicht als Vorlageparameter übergeben. –

2

Der Ansatz 17 C++, dass schön und einfach:

template <std::size_t... A> 
constexpr std::size_t mul = (A * ... * std::size_t(1u)); 

int main() { 
    constexpr std::size_t val = mul<1, 2, 3, 4>; 
} 

Für bestehende C++ Versionen, die Sie teilweise den Fall benötigen mul<v> zu spezialisieren: für Kicks

template <std::size_t... V> struct mul; 
template <std::size_t V> struct mul { 
    statuc constexpr std::size_t value = V; 
}; 
template <std::size_t V, std::size_t... T> struct mul { 
    statuc constexpr std::size_t value = V * mul<T...>::value; 
}; 
template <std::size_t... V> 
using mul_v = mul<V...>::value; 

int main() { 
    constexpr std::size_t v = mul_v<1, 2, 3, 4>; 
} 
+1

Ich nehme an, die C++ 17-Version soll mehr wie '(A * ... * 1U)' sein. – bogdan

+0

@bogdan: ja, in der Tat - derzeit kann ich nicht leicht kompilieren, aber ich hoffe, es ist jetzt korrigiert. Vielen Dank! –

+0

Sie brauchen Parens um die Falte Ausdruck, sie sind obligatorisch in der Grammatik. – bogdan

Verwandte Themen