2015-07-04 12 views
12

ho scritto un modello di classe sulla base di due tipi che viene assegnato un indice univoco sulla base di propri parametri di modello:Risultato inaspettato per un contatore di tipo utilizzando i modelli con i tipi di locali di funzioni in Clang

template<typename SK,typename T> 
struct Component { 
    static uint const index; 
}; 

L'aspettativa è che per ogni nuovo tipo, index viene incrementato:

Component<X,A>::index; // 0 
Component<X,B>::index; // 1 

Component<Y,A>::index; // 0 
Component<Y,B>::index; // 1 
// ...etc 

Il codice completo che assegna gli indici è il seguente:

using uint = unsigned int; 

template<typename SK,typename T> 
struct Component 
{ 
    static uint const index; 
}; 

template<typename SK> 
class ComponentCount 
{ 
    template<typename CSK,typename CT> 
    friend struct Component; 

private: 
    template<typename T> 
    static uint next() { 
     return ComponentCount<SK>::get_counter(); 
    } 

    static uint get_counter() 
    { 
     static uint counter = 0; 
     return counter++; 
    } 
}; 

questo funziona come previsto in GCC (5.1) e MSVC con il seguente test:

// global scope 
struct X {}; 
struct Y {}; 

int main() 
{ 
    // function scope 
    struct Z{}; 

    uint x0 = Component<X,int>::index; 
    uint x1 = Component<X,double>::index; 
    uint x2 = Component<X,double>::index; 
    uint x3 = Component<X,std::string>::index; 
    uint x4 = Component<X,int>::index; 
    uint x5 = Component<X,int>::index; 

    std::cout << x0 << ", " << x1 << ", " << x2 << ", " 
       << x3 << ", " << x4 << ", " << x5 << std::endl; 

    uint y0 = Component<Y,int>::index; 
    uint y1 = Component<Y,double>::index; 
    uint y2 = Component<Y,double>::index; 
    uint y3 = Component<Y,std::string>::index; 
    uint y4 = Component<Y,int>::index; 
    uint y5 = Component<Y,int>::index; 

    std::cout << y0 << ", " << y1 << ", " << y2 << ", " 
       << y3 << ", " << y4 << ", " << y5 << std::endl; 

    uint z0 = Component<Z,int>::index; 
    uint z1 = Component<Z,double>::index; 
    uint z2 = Component<Z,double>::index; 
    uint z3 = Component<Z,std::string>::index; 
    uint z4 = Component<Z,int>::index; 
    uint z5 = Component<Z,int>::index; 

    std::cout << z0 << ", " << z1 << ", " << z2 << ", " 
       << z3 << ", " << z4 << ", " << z5 << std::endl; 

    return 0; 
} 

L'uscita è

0, 1, 1, 2, 0, 0 
0, 1, 1, 2, 0, 0 
0, 1, 1, 2, 0, 0 

Tuttavia con Clang (3.6.1), l'uscita è diverso:

0, 1, 1, 2, 0, 0 
0, 1, 1, 2, 0, 0 
5, 2, 2, 3, 5, 5 

In particolare, gli indici generati per tipi di funzione locale (es. 'Z') fai qualcosa di strano. È come se incrementassero e riassegnassero l'indice ogni volta che viene chiamato Component<Z,...>.

Perché succede? È un bug del compilatore? Ci sono delle considerazioni particolari quando si usano i tipi locali di funzione con i template (post C++ 11)?

Un esempio completo può essere trovato qui: http://coliru.stacked-crooked.com/a/7fcb989ae6eab476

== Modifica ==

ho deciso di postare la questione del clang bugtracker, quindi se qualcun altro si imbatte in questo:

https://llvm.org/bugs/show_bug.cgi?id=24048

+3

Esso si presenta come clang si aspetta l'espressione di inizializzazione per 'componente :: index' a sii puro Rimuovendo l'identificatore 'const' su' index' si ottiene il [risultato previsto] (http://coliru.stacked-crooked.com/a/bc8c910192a2ee67). – Pradhan

+0

Non sono del tutto sicuro se questo programma è ben formato; forse è ben formato ma l'output non è specificato. Ad esempio, le diverse istanze del membro dei dati statici dovrebbero avere un'inizializzazione non ordinata. L'ordine di inizializzazione determina tuttavia l'output del programma. – dyp

+0

Inoltre non è chiaro se l'ordine delle istanze all'interno della funzione è ben definito. Tuttavia, non vedo alcun motivo per il '5' o' 3' di apparire nell'output (che richiederebbe UB credo, e non vedo nessun UB qui, solo un ordine non specificato). Vedi anche https://groups.google.com/a/isocpp.org/d/msg/std-discussion/M6aJMH_ewoM/BpXj_heDGjMJ – dyp

risposta

1

Questo mi sembra un insetto. Non sono a conoscenza delle regole C++ 11 che dovrebbero fare la differenza per tipi locali di funzioni e tipi globali.

Se dump the assembler, si può notare che mentre per X e Y i valori vengono calcolati in realtà, per Z sono precalcolate. Le variabili di guardia per l'inizializzazione della variabile statica del contatore non vengono generate affatto.

.Ltmp87: 
    #DEBUG_VALUE: main:z5 <- 5 
    #DEBUG_VALUE: main:z4 <- 5 
    #DEBUG_VALUE: main:z3 <- 3 
    #DEBUG_VALUE: main:z2 <- 2 
    #DEBUG_VALUE: main:z1 <- 2 
    #DEBUG_VALUE: main:z0 <- 5 
    .loc 6 54 5     # main.cpp:54:5 
    movl std::cout, %edi 
    movl $5, %esi 
    .loc 6 74 5     # main.cpp:74:5 
    callq std::basic_ostream<char, std::char_traits<char> >::operator<<(unsigned int) 
1

Non so se questo è un bug in clang, ma sembra che hai un problema con la versione corrente del componente :: modello successivo.

Per mostrare ciò, ho modificato l'accesso al membro della funzione da "privato" a "pubblico".Poi, con il seguente codice:

for(int i=0; i<5;++i) 
    std::cout<< ComponentCount<int>::next<int>() <<" "; 

ottengo:

Così forse si dovrebbe pensare di cambiare la vostra L'implementazione del prossimo in qualcosa di simile a questo:

template<typename SK> 
class ComponentCount 
{ 
    template<typename CSK,typename CT> 
    friend struct Component; 

private: 
    template<typename T> 
    static uint next() { 
     static uint index = get_counter(); 
     return index; 
    } 

    static uint get_counter() 
    { 
     static uint counter = 0; 
     return counter++; 
    } 
}; 

Con questo ottengo il risultato atteso su gcc, clang3.6 e VS2015

g ++ (GCC) 5.1.0 Copyright (C) 2015 Free Software Foundation, Inc. Questo è un software gratuito; vedere la fonte per le condizioni di copia. Non c'è la garanzia ; nemmeno per COMMERCIABILITÀ o IDONEITÀ PER UN PARTICOLARE SCOPO.

0, 1, 1, 2, 0, 0

0, 1, 1, 2, 0, 0

0, 1, 1, 2, 0, 0

clang versione 3.6.0 (tag/RELEASE_360/def 235480) Obiettivo: x86_64-unknown-linux-gnu modello Discussione: posix

0, 1, 1, 2, 0, 0

0, 1, 1 , 2, 0, 0

0, 1, 1, 2, 0, 0

final code + runs of gcc & clang on coliru

[modifica] errori di battitura

Problemi correlati