7

Ich habe einige C++ 11 Code, der auf Visual Studio 2015 zu kompilieren fehlgeschlagen, die ich kompilieren sollte (und macht mit Clang und GCC):Visual C++: ein Array als Zeiger weiterleiten

#include <utility> 

void test(const char* x); 

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

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

Ich verstehe den Anruf zu forward ist hier nicht notwendig. Dies wird von einem viel komplexeren Code-Code abgeschnitten, der beliebige Arrays in einem variadischen Argument in Zeiger zerlegt und alles weiterleitet. Ich bin mir sicher, dass ich Möglichkeiten finden kann, um mit Template-Spezialisierung oder SFINAE zu umgehen, aber ich würde gerne wissen, ob es C++ gültig ist, bevor ich diesen Weg gehe. Der Compiler ist Visual Studio 2015 und das Problem kann on this online MSVC compiler neu erstellt werden. Der Compiler-Fehler ist:

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])' 

Update:

@Yakk ein Beispiel eher wie diese vorgeschlagen hat:

void test(const char*&& x); 

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

    test(x);  
} 

der einen informativen Fehler gibt:

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 

Wieder , dies kompiliert auf gcc und clang. Die Compilerflags für Visual C++ waren /EHsc /nologo /W4 /c. @Crazy Eddie schlägt vor, dass dies möglicherweise auf eine VC++ Erweiterung zurückzuführen ist, um Provisorien als nicht konstante Referenzen zu übergeben.

+0

Ich würde erwarten, dass es nicht mit der zweiten Version übereinstimmen, weil der Zeiger tatsächlich eine temporäre ist. –

+3

Ist Ihr Compiler im strikten C++ - Modus oder sind * beliebige * Erweiterungen aktiviert? – Yakk

+0

Ein einfacheres Beispiel, das den Unterschied zeigt, ist 'void test (const char * &&) {} const char bob [] =" Hallo "; Test (Bob); '? – Yakk

Antwort

4

Für mich sieht das aus wie ein Fehler in MSVC, wo es versucht, mit Array-to-Pointer clever zu sein und es falsch versteht.

Breaking down Ihr zweites Beispiel:

Der Compiler benötigt const char[13] ein const char*&& von einem L-Wert von Typ zu initialisieren. Um dies zu tun, sagt 8.5.3, dass es ein temporäres vom Typ const char* erstellt und initialisiert es mit dem const char[13], bindet dann den Verweis auf das temporäre.

Das Initialisieren einer aus einer const char[13] beinhaltet eine einfache Array-Zeiger-Konvertierung, die einen prvalue von const char* ergibt, der dann in das temporäre kopiert wird.

Somit ist die Konvertierung wohldefiniert, obwohl MSVC sagt.

In Ihrem ersten Beispiel ist es nicht test(), das das Problem verursacht, aber der Anruf an std::forward. std::forward<const char*> hat zwei Überlastungen, und MSVC beschweren sich weder ist lebensfähig. Die zwei Formen sind

Man nimmt eine Lvalue-Referenz, man nimmt eine Rvalue-Referenz. Bei der Überlegung, ob eine Überladung möglich ist, muss der Compiler eine Konvertierungssequenz von const char[13] zu einer Referenz auf const char* finden.

Da die lvalue-Referenz nicht const ist (es ist ein Verweis auf einen Zeiger auf ein const char; der Zeiger selbst ist nicht const), kann der Compiler die oben beschriebene Konvertierungssequenz nicht anwenden. Tatsächlich ist keine Konvertierungssequenz gültig, da die Konvertierung von Array zu Zeiger ein temporäres erfordert, Sie aber keine nicht konstanten lvalue-Referenzen an Provisorien binden können. Daher hat MSVC das Lvalue-Formular korrekt zurückgewiesen.

Das Rvalue-Formular sollte jedoch, wie oben beschrieben, akzeptiert werden, wird aber von MSVC falsch abgelehnt.

+0

Danke. Ich werde warten und sehen, ob Sie Zeit haben, sich in Ihren letzten Punkt zu vertiefen und einen Fehlerbericht zu schreiben. –

+0

Ich habe die Ausgabe für die erste falsch interpretiert; Ich dachte, es würde sagen, dass es zweideutig war, aber es lehnte beide ab. Ich habe das Problem erläutert. – Falias

+0

Aber der temporäre Zeiger aus dem Array Zerfall ist nicht selbst const, also sollte es nicht mit der (nicht const) rvalue ref Überladung von Forward übereinstimmen? –

0

ich std::decay<const char []>::type glauben ist, was Sie für http://en.cppreference.com/w/cpp/types/decay

+0

Std :: Zerfall ist, was das Problem verursacht. Ich möchte das Array zu einem Zeiger zerlegen, aber wenn ich es tue, bekomme ich dieses Problem. Im Beispielcode habe ich manuell den Array-Teil des Decay (const char [N] -> const char *) auf den Template-Parameter gesetzt, um den Code einfacher und spezifischer für das Problem zu machen. –

-1

Suche ich denke, es sollte kompilieren, aber warum stört Sie std::forward zu benutzen?

Ist das nicht die richtige Lösung einfach

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

mit ersetzen:

(const char*)x 

oder für den generischen Fall ersetzen:

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

mit:

decay_t<decltype(x)>(x) 

Die Verwendung von std::forward scheint hier keinen Zweck zu haben, Sie haben ein Array, Sie wollen es zu einem Zeiger zerlegen, also tun Sie das.

+0

Ich stimme zu, es ist nicht das, was du schreibst. Es ist eine reduzierte Version, so dass ich eine spezifische Frage stellen kann. Zum Beispiel leitet der ursprüngliche Code die Elemente eines Parameter-Packs nach dem Verfall von Arrays weiter (dies wird in der Frage erwähnt, aber vielleicht ist es nicht so klar). Einige der Elemente können also Arrays sein und manche nicht, daher ist der Zerfall erforderlich. Einige können rvalue refs sein (vielleicht nicht kopierbar), einige können lvalues ​​sein, daher die Notwendigkeit der Weiterleitung. –