2017-11-05 3 views
0

Ich habe Schwierigkeiten, consExpr im Kontext eines C++ - eingebauten mehrdimensionalen Arrays zu verstehen. Das folgende Beispiel veranschaulicht mein Problem.consExpr + Multidimension Array schlägt fehl

a) Wenn ich einen Wert zur Kompilierzeit suche, funktioniert das so, wie ich es erwarte, solange ich ihm nichts zuordne.

b) Aber wenn ich versuche, es einer anderen constexpr-Variable zuzuweisen, erhalte ich einen Kompilierzeitfehler.

Ich habe mich umgesehen und sogar das Standarddokument überprüft. Jeder, der mir das erklären kann, ist wirklich ein Guru. FWIW, ich kompiliere dies mit CLang 8.1 unter Xcode mit C++ 14 aktiviert.

using uint8_t = unsigned char; 


#if 1 
enum class safe_numerics_error : uint8_t { 
    success = 0, 
    failure, // result is above representational maximum 
    error_count 
}; 
#else 
// avoiding enum class fails to solve problem 
struct safe_numerics_error { 
    const uint8_t m_t; 
    constexpr const static uint8_t success = 0; 
    constexpr const static uint8_t failure = 1; 
    constexpr safe_numerics_error(uint8_t t) : 
     m_t(t) 
    {} 
    constexpr operator uint8_t() const { 
     return m_t; 
    } 
}; 
#endif 

template<typename R> 
struct checked_result { 
    const safe_numerics_error m_e; 
    const union { 
     const R m_r; 
     char const * const m_msg; 
    }; 
    constexpr /*explicit*/ checked_result(const R & r) : 
     m_e(safe_numerics_error::success), 
     m_r(r) 
    {} 
    constexpr /*explicit*/ checked_result(const safe_numerics_error & e) : 
     m_e(e), 
     m_msg("") 
    {} 
}; 

// integers addition 
template<class T> 
constexpr inline checked_result<T> operator+(
    const checked_result<T> & t, 
    const checked_result<T> & u 
){ 
    // "Constexpr variable 'e' must be initialized by a constant expression" 
    constexpr const safe_numerics_error x[2][2]{ 
     // t == success 
     { 
      // u == ... 
      safe_numerics_error::success, 
      safe_numerics_error::failure 
     }, 
     // t == positive_overflow_error, 
     { 
      // u == ... 
      safe_numerics_error::failure, 
      safe_numerics_error::failure 
     } 
    }; 

#if 1 // compile fails 
    constexpr const safe_numerics_error e = x 
     [static_cast<uint8_t>(t.m_e)] 
     [static_cast<uint8_t>(u.m_e)] 
    ; 

    return 
     (safe_numerics_error::success == e) 
     ? t.m_r + u.m_r 
     : checked_result<T>(e) 
    ; 
#else // works as expected 
    return 
     safe_numerics_error::success == x 
      [static_cast<uint8_t>(t.m_e)] 
      [static_cast<uint8_t>(u.m_e)] 
     ? t.m_r + u.m_r 
     : checked_result<T>(x 
      [static_cast<uint8_t>(t.m_e)] 
      [static_cast<uint8_t>(u.m_e)] 
     ) 
    ; 
#endif 
} 

int main(){ 
    constexpr const checked_result<unsigned> i = 0; 
    constexpr const checked_result<unsigned> j = 0; 

    constexpr const checked_result<unsigned> k = i + j; 

    // return k.m_r; 

    constexpr const checked_result<unsigned> i2 = safe_numerics_error::failure; 
    constexpr const checked_result<unsigned> j2 = 0; 

    constexpr const checked_result<unsigned> k2 = i2 + j2; 
    return k2.m_r; 
} 
+0

Was ist der Fehler? In welcher Zeile? – user463035818

+0

ist der Code, den Ihr Compiler nicht sehen wird (in den '# else' Blöcken) nur Rauschen oder ein Teil der Frage? – user463035818

+0

Zwei Kommentare dazu: 1) Sie geben m_r am Ende von main im Fehlerfall zurück, der ungültig ist, da das Objekt mit einem Fehler konstruiert wurde. 2) Warum nicht einfach & die Enum-Werte anstelle eines 2D-Arrays? d. h. Erfolg & Erfolg == Erfolg, aber Fehler & alles = Fehler – budjmt

Antwort

4

Das Problem ist, dass Sie keine constexpr Variable

constexpr const safe_numerics_error e = x 
    [static_cast<uint8_t>(t.m_e)] 
    [static_cast<uint8_t>(u.m_e)] 

mit einem Wert, der von einem Argument einer Funktion (oder Methode) (als t und u) hängen initialisieren.

Ich weiß, dass Ihr operator+() ein constexpr ist und dass Sie in Ihrem Beispiel es nur verwenden, um constexpr Werte zu initialisieren.

Aber eine constexpr Funktion/Methode kann kompiliert werden und Laufzeit. Daher kann ein Compiler keinen Code in einer constexpr-Funktion/Methode akzeptieren, die nicht zur Laufzeit ausgeführt werden kann.

+0

Diese Antwort fasst es zusammen, aber um zu verdeutlichen: e ist constexpr, egal was, aber wenn die Funktion nicht mit constexpr-Argumenten aufgerufen wird, dann kann diese Variable nicht constexpr sein. Wenn Sie einfach e const deklarieren, sollte es zur Kompilierzeit mit conexpr-Argumenten ausgewertet werden, wird aber keinen Fehler enthalten. – budjmt

+0

@budjmt - nicht sicher zu verstehen, was Sie meinen, aber auch wenn Sie 'e' 'const' (wie in der Frage) erklären, kann die Funktion mit einem non-cont-Wert aufgerufen werden; also kann der Compiler nicht akzeptieren, dass er benutzt wird, um eine 'constexpr'-Variable zu initialisieren (wie auch immer: ich sehe keinen Nutzen darin,' conexpr' eine Variable zu deklarieren, die von einem Eingabeparameter abhängt) Die Funktion wird zur Kompilierzeit ausgeführt, die Variable wird auch zur Kompilierzeit initialisiert, wenn nicht "constexpr" deklariert wird. – max66

+0

genau, es sollte einfach 'const safe_numerics_error e = ...;' ich meinte nur, es soll * nur * const sein, constexpr const. e ist nicht von Natur aus constexpr, es ist nur constexpr zur Kompilierzeit – budjmt