7

Visual Studio 2015でコンパイルに失敗したC++ 11コードを、コンパイルする必要があると思われる以下のコードを削除しました(clangとgccで行います):Visual C++:ポインタを配列として転送

#include <utility> 

void test(const char* x); 

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

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

ここではforwardへの呼び出しは必要ないと思います。これは、より複雑なコードから切り捨てられ、可変引数の配列をポインタにまで減衰させ、すべてを転送します。私は確かにテンプレートの専門化やSFINAEでこれを回避する方法を見つけることができますが、私はその道を行く前に、それが有効なC++かどうかを知りたいと思います。コンパイラはVisual Studio 2015で、問題はon this online MSVC compilerを再現できます。コンパイルエラーは、次のとおりです。

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

更新:@Yakkはもっとこのように例を示唆している

:再び

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 

:より有益なエラーが発生します

void test(const char*&& x); 

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

    test(x);  
} 

これはgccとclangでコンパイルされます。 Visual C++のコンパイラフラグは/EHsc /nologo /W4 /cでした。 @Crazy Eddieは、非const参照として一時変数を渡すためにこれがVC++の拡張機能になっている可能性があることを示唆しています。

+0

ポインタが実際には一時的なので、2番目のバージョンと一致しないことが予想されます。 –

+3

コンパイラは厳密なC++モードですか?*任意の*拡張機能が有効になっていますか? – Yakk

+0

これは、 'void test(const char * &&){} const char bob [] =" hello "のような簡単な例です。テスト(ボブ); '? – Yakk

答えて

4

私にはこれはMSVCのバグのように見えますが、MSVCは配列からポインタへと賢明にしようとし、間違ってしまいます。

あなたの第二の例を分解:

コンパイラはタイプconst char[13]の左辺値からconst char*&&を初期化する必要があります。これを行うために、8.5.3では、一時的なタイプのconst char*を作成し、それをconst char[13]で初期化してから、参照を一時的にバインドします。

const char[13]からconst char*を初期化すると、簡単な配列からポインタへの変換が行われ、const char*のprvalueが生成され、一時的にコピーされます。

したがって、MSVCの記載にかかわらず、変換は明確に定義されています。

最初の例では、問題を引き起こしているのはtest()ではなく、std::forwardへの呼び出しです。 std::forward<const char*>には2つの過負荷があり、MSVCはいずれも実行不能であると訴えています。 2つの形式は、

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

です.1つは左辺値参照をとり、1つは右辺値参照を取ります。いずれかのオーバーロードが実行可能かどうかを検討する場合、コンパイラはconst char[13]からconst char*への参照を変換シーケンスで見つける必要があります。

左辺の参照はconstではないので(const charへのポインタへの参照であり、ポインタ自体はconstではありません)、コンパイラは上で概説した変換シーケンスを適用できません。実際には、変換シーケンスは有効ではありません。なぜなら、配列からポインタへの変換には一時的な変換が必要だが、非constの左辺値参照を一時的にバインドすることはできないからです。したがって、MSVCは左辺形式を拒否するのに正しいです。

ただし、私が上記で設定したような値は受け入れられるべきですが、間違ってMSVCによって拒否されます。

+0

ありがとうございます。私はあなたの最後の点を掘り下げ、バグレポートを提出する時間を得るのを待つでしょう。 –

+0

最初の出力を誤解しました。私はそれがあいまいであると言っていると思ったが、実際にはどちらも完全に拒否していた。問題の説明を更新しました。 – Falias

+0

しかし、配列の減衰からの一時的なポインタはそれ自身constではありませんので、forwardの(非const)rvalue refのオーバーロードと一致しませんか? –

0

私はstd::decay<const char []>::typeはあなたがhttp://en.cppreference.com/w/cpp/types/decay

+0

std :: decayが問題の原因です。私はポインタに配列を崩壊したいが、私はこの問題を抱えている。サンプルコードでは、問題をより簡単で具体的にするために、テンプレートパラメータのdecay(const char [N] - > const char *)の配列部分を手作業で行っています。 –

-1

探しているもので、私はそれをコンパイルすべきだと思うが、なぜあなたはstd::forwardを使用することを悩ませていると考えていますか?置き換え、

(const char*)x 

または一般的なケースのために:

は、単純に

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

を交換するための正しい解決策ではありません

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

で:

decay_t<decltype(x)>(x) 

std::forwardを使用すると、ここに目的がないように見えますが、配列を持っているのでポインタに崩壊したいので、そうしてください。

+0

私は孤立して、あなたが書いたものではないことに同意します。私は特定の質問をすることができるので、それはカットバージョンです。たとえば、元のコードは、配列を崩壊させた後にパラメータパックの要素を転送しています(これはこの質問には言及していますが、それほど明確ではないかもしれません)。だから要素のいくつかは配列かもしれませんし、そうでないかもしれないので、崩壊の必要性があります。いくつかの値は正価(おそらくコピーできない)かもしれませんし、一部は左記かもしれないので、転送の必要性があります。 –