2017-02-23 1 views
2

Ich versuche eine Funktion zu schreiben, die, wenn sie mit Kompilierzeitkonstantenargumenten aufgerufen wird, einen Kompilierfehler auslöst, wenn der Wert des Arguments nicht ist passt nicht zu static_assert, kann aber zur Laufzeit mit berechneten Werten aufgerufen werden.Erzeuge Fehler bei der Kompilierung, wenn der Parameter für die Kompilierzeitkonstante falsch ist

Etwas ein bisschen wie folgt aus:

template<int N> void f(N){ 
    static_assert(N == 5, "N can only be 5."); 
    do_something_with(N); 
} 

void f(int N){ 
    if(N == 5){ 
    do_something_with(N); 
    } 
} 

volatile int five = 5; 
volatile int six = 6; 

int main() { 
    f(5); //ok 
    f(6); //compile-time error 
    f(five); //ok 
    f(six); //run-time abort 

    return 0; 
} 

Wie kann ich das tun? Wenn möglich, würde ich gerne die einfache Syntax f(something) beibehalten können, da dieser Code für eine Bibliothek gedacht ist, die für Anfänger-Programmierer, die nicht vertraut mit Template-Syntax sind, verwendet werden sollte.

+2

Werte, um als Parameter übergeben kann nicht, so dass die Linie 'Vorlage Leere f (N) abgeleitet werden { 'kann nicht korrekt sein –

+1

Kompilierzeit oder Laufzeit. Sie müssen wählen (oder zwei Funktionen ausführen). – xinaiz

+0

Gibt es eine Möglichkeit, es mit "constexpr" anstatt Vorlage Zeug zu tun? – AJMansfield

Antwort

5

Das Beste, was ich mir vorstellen kann, ist eine constexpr Funktion, die eine Ausnahme auslöst.

Wenn zur Kompilierungszeit ausgeführt, verursacht die throw einen Kompilierungsfehler; wenn während der Laufzeit ausgeführt wird, werfen die Ausnahme

Someting wie

#include <stdexcept> 

constexpr int checkGreaterThanZero (int val) 
{ return val > 0 ? val : throw std::domain_error("!"); } 

int main() 
{ 
    // constexpr int ic { checkGreaterThanZero(-1) }; // compile error 

    int ir { checkGreaterThanZero(-1) }; // runtime error 
} 

- EDIT -

Wie Juri kilocheck zeigte, anstatt eine Ausnahme zu werfen, können Sie std::abort() nennen; mit gutem Beispiel

constexpr int checkGreaterThanZero (int val) 
{ return val > 0 ? val : (std::abort(), 0); } 
+1

'std :: abort' ist auch ok. Eine solche Funktion muss jedoch im Kontext von conexpr aufgerufen werden, um zur Kompilierungszeit ausgewertet zu werden. –

+0

@yurikilochek - Ich bin es gewohnt 'throw' zu benutzen, um diese Art von Problemen zu lösen, und ich habe nicht darüber nachgedacht; Du hast recht; Vielen Dank; modifiziert meine Antwort danach – max66

2

mit einer anderen Syntax, können Sie tun:

template <int N> 
using int_c = std::integral_constant<int, N>; 

namespace detail { 
    template <std::size_t N> 
    constexpr int to_number(const int (&a)[N]) 
    { 
     int res = 0; 
     for (auto e : a) { 
      res *= 10; 
      res += e; 
     } 
     return res; 
    } 
} 

template <char ... Ns> 
constexpr int_c<detail::to_number({(Ns - '0')...})> operator ""_c() 
{ 
    return {}; 
} 

#if 1 // Write this way 
// Compile time 
template <int N> void f(int_c<N>) = delete; 
void f(int_c<5>) { do_something_with(5); } 

#else // Or like this 
// Compile time 
template <int N> 
void f(int_c<N>) 
{ 
    static_assert(N == 5, "!"); 
    do_something_with(N); 
} 

#endif 

// Runtime 
void f(int N){ 
    if (N == 5) { 
     std::abort(); // Or any other error handling 
    } 
    f(5_c); 
} 

int main(){ 
    f(5_c); // ok 
    // f(6_c); // Won't compile 
    f(5); // ok 
    f(6); // abort at runtime 
} 

Demo

Verwandte Themen