Perché std :: swap non funziona su elementi <bool> vettoriali in Clang / Win?


14

Ho un codice come questo:

#include <vector>
#include <utility>

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

Argomenti sulla sanità mentale a vector<bool>parte, questo funzionava perfettamente su:

  • Clang per Mac
  • Visual Studio per Windows
  • GCC per Linux

Quindi ho provato a costruirlo con Clang su Windows e ho ricevuto il seguente errore (abbreviato):

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>&&

Sono sorpreso che i risultati differiscano tra le implementazioni.

Perché non funziona con Clang su Windows?


Quindi immagino che il chiarimento necessario sia: è il risultato di operator[]un valore? e può std::swapoperare su valori e valori x?
Mgetz,

@Mgetz Sì. No. In quell'ordine. Questa domanda è stata posta "per davvero" in privato l'altro giorno, e ho pensato che fosse sufficientemente divertente che la risposta fosse "Clang / Win non è stata infranta; il codice è stato infranto per tutto questo tempo ma le combo di toolchain tradizionali non si sono mai preoccupate di dirtelo "per scriverlo qui: P
Lightness Races in Orbit

2
Proprio come un FYI, questo non viene compilato in VS 2019 con /permissive-(conformità), che dovrebbe essere generalmente utilizzato comunque;)
ChrisMM

1
@ChrisMM Infatti! La modalità di conformità disattivata faceva parte del puzzle. (Anche se non lo sapevamo prima di esaminarlo!) E la mia risposta lo sottolinea: P
Razze di leggerezza in orbita

Risposte:


15

Lo standard non richiede che questo si compili su nessuna toolchain!

Per prima cosa ricorda che vector<bool>è strano e la sottoscrizione ti dà un oggetto temporaneo di un tipo proxy chiamato std::vector<bool>::reference, piuttosto che un effettivo bool&.

Il messaggio di errore indica che non è possibile associare questo temporaneo a un constriferimento non di valore nell'implementazione generica template <typename T> std::swap(T& lhs, T& rhs).

Estensioni!

Tuttavia, risulta che libstdc ++ definisce un sovraccarico per std::swap(std::vector<bool>::reference, std::vector<bool>::reference), ma questa è un'estensione dello standard (o, se è presente, non riesco a trovare alcuna prova).

Anche libc ++ fa questo .

Immagino che l'implementazione stdlib di Visual Studio, che stai ancora usando, non lo fa , ma poi per aggiungere un insulto a un infortunio puoi associare i temporali ai riferimenti lvalue in VS (a meno che tu non stia usando la modalità di conformità), quindi il la funzione standard, "generica", std::swapfunziona fino a quando non si sostituisce il compilatore VS al compilatore Clang più rigoroso.

Di conseguenza, hai fatto affidamento sulle estensioni su tutte e tre le toolchain per le quali ha funzionato per te e la combinazione Clang su Windows è l'unica che mostra effettivamente una rigorosa conformità.

(Secondo me, queste tre toolchain avrebbero dovuto diagnosticare questo, quindi non hai spedito codice non portatile per tutto il tempo. 😊)

E adesso?

Potrebbe essere allettante aggiungere la tua specializzazione di std::swape std::vector<bool>::reference, ma non ti è permesso farlo per i tipi standard; in effetti, sarebbe in conflitto con i sovraccarichi che libstdc ++ e libc ++ hanno scelto di aggiungere come estensioni.

Quindi, per essere portatile e conforme, è necessario modificare il codice .

Forse un buon vecchio stile:

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

Oppure utilizza la speciale funzione di membro statico che fa esattamente quello che volevi :

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

Anche ortografico come segue:

vb.swap(vb[0], vb[1]);

Per quanto riguarda il fatto, ma non è previsto che AFAIK possano farlo. Finché non infrangono il codice conforme, possono estendere l'implementazione per rendere "OK" il codice non funzionante.
NathanOliver,

@ NathanOliver-ReinstateMonica Bene, ok. Non devono almeno diagnosticare l'uso di queste cose, però? eel.is/c++draft/intro.compliance#8
Razze di leggerezza in orbita

@LightnessRaceswithMonica c'è qualche lingua che proibisce questa estensione?
Mgetz,

@Mgetz Siamo spiacenti, non sono esperto in tutte le lingue esistenti, quindi non posso rispondere a questa domanda
Razze di leggerezza in orbita

Non sono sicuro se si applicano estensioni non conformi a questo documento . Hanno aggiunto un sovraccarico che richiede un std::vector<bool>::referencequindi nulla è in realtà mal formato. Per me sembra che usare qualcosa di simile char * foo = "bar";richiederebbe una diagnosi poiché è mal formata.
NathanOliver,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.