16

次のようなコードがあります。

#include <vector>
#include <utility>

int main()
{
   std::vector<bool> vb{true, false};
   std::swap(vb[0], vb[1]);
}

脇の正気についての議論vector<bool>、これはうまく機能していました:

  • Mac用のクラン
  • Visual Studio for Windows
  • Linux 用 GCC

次に、Windows で Clang を使用してビルドしようとしたところ、次のエラー (要約) が表示されました。

error: no matching function for call to 'swap'
                                std::swap(vb[0], vb[1]);
                                ^~~~~~~~~

note: candidate function [with _Ty = std::_Vb_reference<std::_Wrap_alloc<std::allocator<unsigned int> > >, $1 = void] not viable: expects an l-value for 1st argument
inline void swap(_Ty& _Left, _Ty& _Right) _NOEXCEPT_COND(is_nothrow_move_constructible_v<_Ty>&&

実装によって結果が異なることに驚いています。

Windows の Clang で動作しないのはなぜですか?

4

1 に答える 1

18

標準では、これをツールチェーンでコンパイルする必要はありません!

最初に思い出してください。これvector<bool>は奇妙で、添え字を付けるとstd::vector<bool>::reference、実際の ではなく、というプロキシ タイプの一時オブジェクトが得られますbool&

エラーメッセージは、この一時的なものをジェネリック実装constの左辺値以外の参照にバインドできないことを示しています。template <typename T> std::swap(T& lhs, T& rhs)

拡張機能!

ただし、libstdc++は のオーバーロードを定義していることが判明しましたstd::swap(std::vector<bool>::reference, std::vector<bool>::reference)が、これは標準の拡張です (または、そこにある場合、その証拠を見つけることができません)。

libc++もこれを行います

まだ使用しているVisual Studio stdlibの実装はそうではないと思いますが、傷害に侮辱を加えるために、VSの左辺値参照に一時変数をバインドできます(適合モードを使用していない場合)。標準の「ジェネリック」std::swap関数は、VS コンパイラをより厳密な Clang コンパイラに置き換えるまで機能します。

その結果、3 つのツールチェーンすべてで機能する拡張機能に依存してきました。Windows 上の Clang の組み合わせは、実際に厳格なコンプライアンスを示す唯一のものです。

(私の意見では、これらの 3 つのツールチェーンがこれを診断する必要があったため、移植性のないコードをずっと出荷していませんでした。)

今何?

std::swapandの独自の特殊化を追加したくなるかもしれませstd::vector<bool>::referenceんが、標準型に対してこれを行うことは許可されていません。実際、libstdc++ と libc++ が拡張機能として追加することを選択したオーバーロードと競合します。

したがって、移植可能で準拠するには、コードを変更する必要があります

おそらく古き良き時代:

const bool temp = vb[0];
vb[0] = vb[1];
vb[1] = temp;

または、まさにあなたが望んでいたことを行う特別な静的メンバー関数を利用してください:

std::vector<bool>::swap(vb[0], vb[1]);

次のように綴ることもできます。

vb.swap(vb[0], vb[1]);
于 2019-11-01T13:39:49.567 に答える