Per quanto posso dire, questo è un bug clang.
L'inizializzazione delle liste di copia ha un comportamento piuttosto non intuitivo: considera i costruttori espliciti come vitali fino a quando la risoluzione di sovraccarico non è completamente terminata, ma può quindi rifiutare il risultato di sovraccarico se viene scelto un costruttore esplicito. La formulazione in un post-N4567 progetto, [over.match.list] p1
In copia-list-inizializzazione, se viene scelta un costruttore explicit
, l'inizializzazione è mal-formata. [Nota: Questo differisce dalle altre situazioni (13.3.1.3, 13.3.1.4), dove solo i costruttori di conversione sono considerati per l'inizializzazione della copia.Questa limitazione si applica solo a se questa inizializzazione fa parte del risultato finale della risoluzione di sovraccarico . - nota end]
clang TESTA accetta il seguente programma:
#include <iostream>
using namespace std;
struct String1 {
explicit String1(const char*) { cout << "String1\n"; }
};
struct String2 {
String2(const char*) { cout << "String2\n"; }
};
void f1(String1) { cout << "f1(String1)\n"; }
void f2(String2) { cout << "f2(String2)\n"; }
void f(String1) { cout << "f(String1)\n"; }
void f(String2) { cout << "f(String2)\n"; }
int main()
{
//f1({"asdf"});
f2({"asdf"});
f({"asdf"});
}
Che è, tranne che per commentare la chiamata a f1
, direttamente da Bjarne Stroustrup di N2532 - Uniform initialization, Capitolo 4. Grazie a Johannes Schaub per avermi mostrato questo articolo su std-discussion.
Lo stesso capitolo contiene la seguente spiegazione:
Il vero vantaggio di explicit
è che rende f1("asdf")
un errore . Un problema è che la risoluzione di sovraccarico "preferisce" i costruttori non , in modo che f("asdf")
chiami f(String2)
. Considero la risoluzione di f("asdf")
meno ideale, perché lo scrittore di String2
probabilmente non intendeva risolvere le ambiguità a favore di String2
(almeno non in tutti i casi in cui espliciti e non esplicite costruttori si verificano come questo) e il scrittore di String1
certamente no. La regola favorisce i "programmatori sciatti" che non usano explicit
.
Per quanto ne so, N2640 - Initializer Lists — Alternative Mechanism and Rationale è l'ultima carta che include logica di questo tipo di risoluzione di sovraccarico; il successore N2672 è stato votato nella bozza C++ 11.
Dal suo capitolo "The Meaning Of Explicit":
Un primo approccio per fare l'esempio mal formata è quella di richiedere che tutte le costruttori (esplicite e non espliciti) sono considerati per impliciti conversioni , ma se un costruttore esplicito finisce per essere selezionato, quel programma è mal formato. Questa regola può introdurre le sue sorprese; per esempio:
struct Matrix {
explicit Matrix(int n, int n);
};
Matrix transpose(Matrix);
struct Pixel {
Pixel(int row, int col);
};
Pixel transpose(Pixel);
Pixel p = transpose({x, y}); // Error.
Un secondo approccio è quello di ignorare i costruttori espliciti quando si cerca per la vitalità di una conversione implicita, ma di includere quando in realtà la selezione del costruttore di conversione: se un costruttore esplicito finisce essendo selezionato, il programma è mal formato. Questo approccio alternativo consente di utilizzare l'ultimo esempio (Pixel-vs-Matrix) come previsto (transpose(Pixel)
è selezionato), mentre l'esempio originale ("X x4 = { 10 };
") è mal formato.
Mentre questo documento propone di utilizzare il secondo approccio, la sua formulazione sembra essere viziata - nella mia interpretazione del testo, non produce il comportamento descritto nella parte razionale della carta. La formulazione è stata rivista in N2672 per utilizzare il primo approccio, ma non sono riuscito a trovare alcuna discussione sul perché questo è stato modificato.
C'è naturalmente un po 'più formulazione coinvolti in inizializzazione di una variabile come nel PO, ma considerando la differenza di comportamento tra clang e gcc è la stessa del primo programma di esempio nella mia risposta, credo che questo coperchi i punti principali.
MSVS 2015 compila anche questo. – NathanOliver
Sembra molto simile alla descrizione del problema qui: https://gcc.gnu.org/ml/gcc-help/2014-02/msg00004.html che ha portato a http://gcc.gnu.org/bugzilla/show_bug .cgi? id = 60027 che è irrisolto/non commentato finora. – kfunk
Interessante. Copy-list-init dovrebbe considerare tutti i costruttori e essere mal formato se viene scelto un costruttore esplicito. La domanda è se questo significa che una sequenza di conversione implicita non può essere formata, o se può essere formata e quindi l'inadeguatezza entra in gioco. Nel primo caso non sarebbe ambiguo; nel secondo caso è ambiguo. –