7

Ho tagliato giù qualche codice C++ 11 che è stato non riesce a compilare su Visual Studio 2015 per il seguito della quale credo dovrebbe compilare (e lo fa con clangore e gcc):Visual C++: trasmettere un array come un puntatore

#include <utility> 

void test(const char* x); 

int main() 
{ 
    const char x[] = "Hello world!"; 

    test(std::forward<const char*>(x));  
} 

Capisco che la chiamata a forward non è necessaria qui. Questo è ridotto da un bit di codice molto più complesso che decompone qualsiasi array in un argomento variadico fino a puntatori e inoltra tutto avanti. Sono sicuro che posso trovare dei modi per aggirare questo problema con la specializzazione dei modelli o SFINAE, ma mi piacerebbe sapere se è C++ valido prima di percorrere questa strada. Il compilatore è Visual Studio 2015 e il problema può essere ricreato on this online MSVC compiler. L'errore di compilazione è:

main.cpp(13): error C2665: 'std::forward': none of the 2 overloads could convert all the argument types 
c:\tools_root\cl\inc\type_traits(1238): note: could be '_Ty &&std::forward<const char*>(const char *&&) noexcept' 
     with 
     [ 
      _Ty=const char * 
     ] 
c:\tools_root\cl\inc\type_traits(1231): note: or  '_Ty &&std::forward<const char*>(const char *&) noexcept' 
     with 
     [ 
      _Ty=const char * 
     ] 
main.cpp(13): note: while trying to match the argument list '(const char [13])' 

Aggiornamento:

@Yakk ha suggerito un esempio più simile a questo:

void test(const char*&& x); 

int main() 
{ 
    const char x[] = "Hello world!"; 

    test(x);  
} 

che dà un errore più informativo:

main.cpp(7): error C2664: 'void test(const char *&&)': cannot convert argument 1 from 'const char [13]' to 'const char *&&' 
main.cpp(7): note: You cannot bind an lvalue to an rvalue reference 

Anche in questo caso , questo compila su gcc e clang. I flag del compilatore per Visual C++ erano /EHsc /nologo /W4 /c. @Crazy Eddie suggerisce che questo potrebbe essere dovuto ad un'estensione VC++ per passare i temporaries come riferimenti non const.

+0

Mi aspetto che non corrisponda alla seconda versione perché il puntatore è in realtà un temporaneo. –

+3

Il compilatore è in modalità C++ rigorosa o ha * eventuali * estensioni abilitate? – Yakk

+0

Un esempio più semplice che mostra la differenza potrebbe essere 'void test (const char * &&) {} const char bob [] =" ciao "; prova (bob); '? – Yakk

risposta

4

Per me questo sembra un bug in MSVC in cui cerca di essere intelligente con array-to-pointer e sbaglia.

Abbattere il tuo secondo esempio:

Il compilatore deve inizializzare un const char*&& da un lvalue di tipo const char[13]. Per fare questo, 8.5.3 dice che crea un temporaneo di tipo const char* e lo inizializza con lo const char[13], quindi lega il riferimento al temporaneo.

L'inizializzazione di const char* da un const char[13] implica una semplice conversione da matrice a puntatore, con un valore di const char* che viene quindi copiato nel temporaneo.

Quindi la conversione è ben definita, nonostante ciò che MSVC dice.

Nel primo esempio, non è il test() a causare il problema, ma la chiamata a std::forward. std::forward<const char*> ha due sovraccarichi e MSVC si lamenta che nessuno dei due è praticabile. Le due forme sono

const char*&& std::forward(const char*&&); 
const char*&& std::forward(const char*&); 

Uno prende un riferimento di lvalue, uno prende un riferimento di rvalue. Quando si considera se il sovraccarico sia fattibile, il compilatore deve trovare una sequenza di conversione da const char[13] a un riferimento a const char*.

Poiché il riferimento lvalue non è const (è un riferimento a un puntatore a un const char, il puntatore stesso non è const), il compilatore non può applicare la sequenza di conversione descritta sopra. In effetti, nessuna sequenza di conversione è valida, poiché la conversione da matrice a puntatore richiede un valore temporaneo, ma non è possibile associare riferimenti a valori non costanti ai provvisori. Quindi MSVC ha ragione nel rifiutare il modulo lvalue.

Il modulo di rvalue, tuttavia, come ho stabilito sopra, deve essere accettato ma è erroneamente rifiutato da MSVC.

+0

Grazie. Aspetterò e vedrò se hai tempo per scavare nel tuo ultimo punto, e guarda la segnalazione di un bug. –

+0

Ho interpretato erroneamente l'output per il primo; Pensavo che dicesse che era ambiguo, ma in realtà rifiutava entrambi. Ho aggiornato spiegando il problema. – Falias

+0

Ma il puntatore temporaneo dal decadimento dell'array non è esso stesso const, quindi non dovrebbe corrispondere al sovraccarico del refval (non costante) di forward? –

0

Credo std::decay<const char []>::type è quello che stai cercando http://en.cppreference.com/w/cpp/types/decay

+0

std :: decay è ciò che sta causando il problema. Voglio decadere l'array su un puntatore, ma quando lo faccio ottengo questo problema. Nel codice di esempio ho eseguito manualmente la parte dell'array del decadimento (const char [N] -> const char *) sul parametro template da inoltrare per rendere il codice più semplice e specifico al problema. –

-1

penso che dovrebbe compilare, ma perché ti dà fastidio da usare std::forward?

non è la soluzione giusta per sostituire semplicemente

std::forward<const char*>(x) 

con:

(const char*)x 

o per il caso generico, sostituire:

std::forward<decay_t<decltype(x)>>(x) 

con:

decay_t<decltype(x)>(x) 

L'utilizzo di std::forward non sembra avere alcuno scopo qui, hai un array, vuoi decaderlo su un puntatore, quindi fallo.

+0

Sono d'accordo, da solo, non è quello che scriveresti. È una versione ridotta, quindi posso porre una domanda specifica. Ad esempio, il codice originale sta inoltrando gli elementi di un pacchetto di parametri dopo aver decaduto qualsiasi array (lo menziona nella domanda, ma forse non è così chiaro). Quindi alcuni degli elementi possono essere array e altri no, da qui la necessità di decadenza. Alcuni possono essere ref valori (forse non copiabili), alcuni possono essere lvalue, quindi la necessità di inoltro. –

Problemi correlati