2013-06-05 22 views
20
#include <iostream> 

struct X { 
    X(std::initializer_list<int> list) { std::cout << "list" << std::endl; } 
    X(float f) { std::cout << "float" << std::endl; } 
}; 

int main() { 
    int x { 1.0f }; 
    X a(1);  // float (implicit conversion) 
    X b{1};  // list 
    X c(1.0f); // float 
    X d{1.0f}; // list (narrowing conversion) ARG!!! 

    // warning: narrowing conversion of '1.0e+0f' from 'float' to 'int' 
    // inside { } [-Wnarrowing] 
} 

C'è un altro modo di rimuovere std::initializer_list da un elenco di sovraccarico (vale a dire, rendendo i non-list ctors più favorevole) invece di utilizzare il -initialization(), o meno proibendo che avvenga la conversione del restringimento (oltre a trasformare l'avviso in errore)?Prevenire restringimento di conversione quando si utilizza std :: initializer_list

Stavo usando il compilatore http://coliru.stacked-crooked.com/ che utilizza GCC 4.8.

+1

Buona domanda, +1. Ma è una classe interessante che può essere costruita da una quantità arbitraria di interi o esattamente da un numero in virgola mobile. – Angew

+2

@Angew: eviterei di giudicare i casi di test ridotti troppo avidamente, ad esempio sapevi di [std :: discrete_distribution] (http://en.cppreference.com/w/cpp/numeric/random/discrete_distribution/discrete_distribution)? Non esattamente identico, ovviamente, ma mostra ancora perché potrebbe essere desiderabile. –

+0

Herb Sutter ha coperto questo di recente. Poiché è stata utilizzata l'inizializzazione delle parentesi, è preferibile la versione dell'elenco di inizializzazione. Se si desidera il costruttore float, è necessario utilizzare le parentesi. Vedi esempi (d) e (e) qui: http://herbsutter.com/2013/05/09/gotw-1-solution/ –

risposta

19

In realtà, un programma contenente una conversione di restringimento in un inizializzatore di coppie di parentesi è non formattato. Non sono sicuro del motivo per cui il compilatore ti dà solo un avviso, ma sicuramente dovrebbe generare un errore qui (FWIW, Clang does that).

Si noti inoltre, che si tratta di una conversione restringimento (e quindi illegale) così:

int x { 1.0f }; // ERROR! Narrowing conversion required 

al comma 8.5.4/3 del C++ 11 standard:

List-initialization of an object or reference of type T is defined as follows:

— If T is an aggregate, aggregate initialization is performed (8.5.1). [...]

— Otherwise, if the initializer list has no elements [...]

— Otherwise, if T is a specialization of std::initializer_list<E> , [...]

— Otherwise, if T is a class type, constructors are considered. The applicable constructors are enumerated and the best one is chosen through overload resolution (13.3, 13.3.1.7). If a narrowing conversion (see below) is required to convert any of the arguments, the program is ill-formed. [...]

Per essere più precisi, lo Standard dice solo che in questo caso è necessaria una "diagnostica" e un avvertimento è diagnostico, quindi il comportamento del compilatore è conforme, ma credo che emettere un errore sarebbe un comportamento migliore.

+0

+1, ero convinto che questo dovrebbe essere un errore. – juanchopanza

+1

Le conversioni restringenti ora si applicano agli array C ([esempio] (http://coliru.stacked-crooked.com/view?id=c45787e3e4535b20bcc37c6d62a9e074-e54ee7a04e4b807da0930236d4cc94dc)). Pertanto, facendo un restringimento delle conversioni un errore potrebbe rompere il codice esistente. – Morwenn

+1

@Morwenn: È vero, ma in questo caso non inizializziamo un aggregato –

3

Sembra un errore del compilatore. Dovresti ricevere un errore invece di un avviso. L'inizializzazione delle parentesi non dovrebbe mai implicitamente restringersi.

Dalla standard (§ 8.5.4)

struct B { 
    B(std::initializer_list<int>); 
}; 
B b1 { 1, 2 }; // creates initializer_list<int> and calls constructor 
B b2 { 1, 2.0 }; // error: narrowing 
+1

Lo standard richiede una diagnostica, quindi l'emissione di un avviso è conforme. Puoi usare '-pedantic-errors' o' -Werror = narrowing' se vuoi un errore. –

2

è possibile ottenere ciò che si vuole con std::enable_if.

#include <iostream> 
#include <type_traits> 

struct X { 
    template<typename T, typename = typename std::enable_if<std::is_same<T,int>::value>::type> 
    X(std::initializer_list<T>) { std::cout << "list" << std::endl; } 
    X(float) { std::cout << "float" << std::endl; } 
}; 

int main() { 
    X a(1);  // float (implicit conversion) 
    X b{1};  // list 
    X c(1.0f); // float 
    X d{1.0f}; // float (yay) 
} 

funziona sia su g++4.8 e clang 3.2

+0

'enable_if' è il modo in cui lo rimuovi dalla lista dei candidati, ma non penso che' is_same' sia la condizione giusta qui (previene l'allargamento e il restringimento). –

+0

Hai ragione, certo, non ci ho pensato. Penso che 'std :: is_same :: type, int> :: value' funzionerebbe comunque. – krzaq

0

È possibile utilizzare -Wno-c++11-narrowing per spegnere gli errori:

Ecco un programma di test di esempio:

#include <cstdint> 

struct foo { 
    int32_t a; 
}; 

void foo(int64_t val) { 
    struct foo A = { val }; 
} 

Compila con clangore ++ - 3.8 con solo -std=c++11, otteniamo l'errore dichiarato:

enter image description here

Aggiungi -Wno-c++11-narrowing, il silenzio d'oro :-)

enter image description here

Naturalmente, la questione restringimento potrebbe tornare a mordere più tardi, ma potrebbe a volte essere più facile per ritardare il dolore debito tecnico fino a più tardi. ymmv :-)

Problemi correlati