Der folgende Code in Visual Studio kompiliert 2013 gcc 4.8, klirren 3.4 und Klirren 3.5 (Apple-LLVM 6.0), aber nicht kompiliert in Klirren 3.6 (via Apple LLVM 6.1)Ist diese Änderung in der Überladungsauflösung zwischen Clang 3.5 und 3.6 korrekt oder ein Fehler?
Der Code ist eine vereinfachte Version eines kompliziert Klasse in unserer Codebasis, die mindestens erforderlich ist, um das Problem zu zeigen.
Der Kern des Problems ist, dass die Kopie Konstruktion von TYPED_VALUE
ist, in 3,6, für den Typ des templated Umwandlungsoperator Auswertung STRING
wegen der Anwesenheit eines Konstruktor, der ein STRING
annimmt; Dies führt dazu, dass std::is_constructible
ausgewertet wird, was dazu führt, dass die Definition STRING
benötigt wird (was wir hier nicht bereitstellen können - würde zu einer zirkulären Abhängigkeit im vollständigen Code führen).
class STRING;
class TYPED_VALUE
{
public:
TYPED_VALUE(const TYPED_VALUE&) = default; // explicit or implicit doesn't make a difference
TYPED_VALUE(const STRING &) {}
template< typename TYPE, typename std::enable_if<!std::is_pointer<TYPE>::value && !std::is_constructible< TYPE, const STRING& >::value && !std::is_constructible< TYPE, bool >::value, int >::type = 0 >
operator TYPE(void) const = delete;
};
class TYPED_STORAGE
{
public:
TYPED_STORAGE(const TYPED_VALUE &v) : value(v) {}
TYPED_VALUE value;
};
Die Fehlermeldung ist
/type_traits:2329:38: error: incomplete type 'SICORE::STRING' used in type trait expression
: public integral_constant<bool, __is_constructible(_Tp, _Args...)>
^
/main.cpp:348:99: note: in instantiation of template class 'std::__1::is_constructible<SICORE::STRING, const SICORE::STRING &>' requested here
template< typename TYPE, typename std::enable_if<!std::is_pointer<TYPE>::value && !std::is_constructible< TYPE, const STRING& >::value && !std::is_constructible< TYPE, bool >::value, int >::type = 0 >
^
/main.cpp:349:9: note: while substituting prior template arguments into non-type template parameter [with TYPE = SICORE::STRING]
operator TYPE(void) const = delete;
^~~~~~~~~~~~~~~~~~~~~~~~~~~
/main.cpp:355:56: note: while substituting deduced template arguments into function template 'operator type-parameter-0-0' [with TYPE = SICORE::STRING, $1 = (no value)]
TYPED_STORAGE(const TYPED_VALUE &v) : value(v) {}
^
/main.cpp:340:11: note: forward declaration of 'SICORE::STRING'
class STRING;
^
Für mich wie ein Fehler in 3.6 scheint, in früheren Versionen der Überladungsauflösung der Kopierkonstruktor feststellt, dass die Template-Argumente zu bewerten, ohne dass die beste Lösung ist - Ich habe versucht, die Überladung Auflösung Hinweise in der Norm zu verstehen, aber ich denke, dass nur verwirrt mich mehr;)
(Dies kann behoben werden, indem entweder der Konstruktor oder der Konvertierungsoperator explizit ich realisiere, aber das ist nicht das Verhalten wir wollen)
Irgendwelche Standardexperten da draußen kennen die Antwort?
Dies kann unter [temp.inst]/7 fallen "Wenn der Überladungsauflösungsprozess die korrekte Funktion zum Aufrufen ohne Vorlage einer Klassen- Vorlagendefinition bestimmen kann, ist nicht angegeben, ob diese Instanziierung tatsächlich stattfindet." – dyp
Um @ dyps Kommentar zu erweitern, funktioniert die Überladungsauflösung im Allgemeinen in drei Schritten: 1) Aufzählung aller möglichen Kandidatenfunktionen; 2) Bestimmen der Umwandlungssequenzen, die für jedes Argument für jeden Kandidaten erforderlich sind (und Entfernen nicht lebensfähiger Kandidaten); 3) vergleiche die Umwandlungssequenzen für die beste Übereinstimmung. Wenn wir diese Prozedur bis zum Buchstaben befolgen, dann wird das 'is_constructible' * immer * instanziiert, aber der Standard gibt dem Compiler Spielraum, ihn nicht zu instantiieren, wenn er die richtige Funktion zum Aufrufen bestimmen kann. –
Ok danke Jungs, ich werde mir überlegen wie ich den gewünschten Effekt auf andere Weise erreichen kann! –