2016-05-12 22 views
14

Perché questa funzione membro constexprstatic, identificata dal commento //! Nah, non viene visualizzata come constexpr quando viene chiamata?Perché questa funzione membro statico di constexpr non è vista come constexpr quando viene chiamata?

struct Item_id 
{ 
    enum Enum 
    { 
     size, position, attributes, window_rect, max_window_size, _ 
    }; 

    static constexpr int n_items_ = _;       // OK 
    constexpr auto member_n_items() const -> int { return _; } // OK 
    static constexpr auto static_n_items() -> int { return _; } // OK 
    static constexpr int so_far = n_items_;      // OK 
    #ifndef OUT_OF_CLASS 
     static constexpr int bah = static_n_items();   //! Nah. 
    #endif 
}; 

constexpr auto n_ids() -> int { return Item_id().member_n_items(); } // OK 

auto main() -> int 
{ 
    #ifdef OUT_OF_CLASS 
     static constexpr int bah = Item_id::static_n_items(); // OK 
    #endif 
} 

MinGW g ++ 5.1 rapporti

 
constexpr.cpp:12:46: error: 'static constexpr int Item_id::static_n_items()' called in a constant expression 
    static constexpr int bah = static_n_items();    //! Nah. 

Visual C++ 2015 rapporti

 
constexpr.cpp(12): error C2131: expression did not evaluate to a constant 
constexpr.cpp(12): note: failure was caused by call of undefined function or one not declared 'constexpr' 
constexpr.cpp(12): note: see usage of 'Item_id::static_n_items' 

mio editor di testo insiste sul fatto che il nome nella chiamata è lo stesso del nome nella definizione della funzione.

Sembra avere qualcosa a che fare con la classe incompleta, perché con OUT_OF_CLASS ha definito la compilazione gradevole.

Ma allora perché funzionano i dati n_items_ e, perché tale regola (non ha senso per me)?

+1

gcc6.1 e clang3.8 non mi piace. –

+1

Nota, spostando la dichiarazione e inizializzazione di 'bah' fuori da' struct' e facendo 'static constexpr int bah = Item_id :: static_n_items();' rende questo compilato su G ++ 5.3 –

+0

@GillBates: Grazie, ho trovato lo stesso (vedi la domanda rivista). È strano. –

risposta

11

Dalla memoria, i corpi delle funzioni dei membri vengono valutati solo dopo che la classe è stata completamente definita.

static constexpr int bah = static_n_items(); 

fa parte della definizione della classe, ma è riferita ad una (statico) funzione membro, che non può ancora essere definito.

Soluzione:

rinviare espressioni costanti a una classe base e derivano da esso.

es .:

struct Item_id_base 
{ 
    enum Enum 
    { 
     size, position, attributes, window_rect, max_window_size, _ 
    }; 

    static constexpr int n_items_ = _;       // OK 
    constexpr auto member_n_items() const -> int { return _; } // OK 
    static constexpr auto static_n_items() -> int { return _; } // OK 
    static constexpr int so_far = n_items_;      // OK 
}; 

struct Item_id : Item_id_base 
{ 
    #ifndef OUT_OF_CLASS 
     static constexpr int bah = static_n_items();   // now OK 
    #endif 
}; 

constexpr auto n_ids() -> int { return Item_id().member_n_items(); } // OK 

auto main() -> int 
{ 
    #ifdef OUT_OF_CLASS 
     static constexpr int bah = Item_id::static_n_items(); // OK 
    #endif 
} 

Perché pensi che la norma non consente esso?

Perché questo è illegale:

struct Item_id 
{ 
    // ... etc. 

    #ifndef OUT_OF_CLASS 
     static constexpr int bah;// = static_n_items();   //! Nah. 
    #endif 
}; 

constexpr int Item_id::bah = static_n_items(); 

E un constexpr deve avere una definizione constexpr. L'unico posto che possiamo definirlo è durante la sua dichiarazione ...

... così deducendolo non può riferirsi a nessuna funzione il cui corpo non è ancora definito.

Non riesco a capire dove cercare lo standard per tutto questo. Probabilmente 5 diverse, clausole apparentemente non correlati :)

+2

Penso che probabilmente hai ragione, e grazie !, ma ho sempre pensato alla riscrittura (con le definizioni di funzione dopo la classe) come solo uno strumento concettuale. Non riesco a ricordare di averlo visto nello standard. Quindi, beh, hai un'idea di quale parte dello standard lo specifica? –

+0

@ Cheersandhth.-Alf cercandolo ora ... –

+1

@ Cheersandhth.-Alf risposta un po 'tiepida aggiunta alla risposta. Non ho lo stomaco per scimmiottare lo standard per tutte le clausole pertinenti alla fine di una lunga giornata ... Sono sicuro che tu conosci la sensazione! –

4

[class.mem]/2

All'interno della classe membro-specifica, la classe è considerata completa in seno agli organi di funzione, gli argomenti di default, eccezione -specifiche e inizializzatori di membri predefiniti (comprese le cose nelle classi annidate). Altrimenti è considerato incompleto all'interno della sua classe specifica membro.

Nell'inizializzatore di un membro di dati static di una classe, la classe è incompleta.L'inizializzatore può vedere solo le dichiarazioni dei membri che lo precedono e tutte le funzioni membro che può vedere sono considerate dichiarate ma non definite. Una chiamata a una funzione dichiarata ma non definita non può essere un'espressione costante.

+0

Grazie per averlo scoperto! –

+0

[basic.scope.class]/1 dice quasi la stessa cosa in un linguaggio leggermente diverso, ma non ho trovato un riferimento per supportare la mia affermazione che "qualsiasi funzione membro che può vedere è considerata dichiarata ma non definita". – Oktalist

Problemi correlati