const auto std::initializer_list Unterschied zwischen Clang und GCC

Lesezeit: 5 Minuten

const auto stdinitializer list Unterschied zwischen Clang und GCC
daveraja

Ich versuche zu verstehen, was das richtige Verhalten von C++11 sein sollte, wenn Initialisierungslisten kombiniert werden und const auto. Ich erhalte ein unterschiedliches Verhalten zwischen GCC und Clang für den folgenden Code und möchte wissen, welcher der richtige ist:

#include <iostream>
#include <typeinfo>
#include <vector>

int main()
{
    const std::initializer_list<int> l1 = { 1, 2, 3 };
    const auto l2 = { 1, 2, 3 };

    std::cout << "explicit: " << typeid(l1).name() << std::endl;
    std::cout << "auto:     " << typeid(l2).name() << std::endl;
}

Kompiliert mit g++ ist die Ausgabe:

explicit: St16initializer_listIiE
auto:     St16initializer_listIKiE

Während die kompilierte Version von clang++ Folgendes erzeugt:

explicit: St16initializer_listIiE
auto:     St16initializer_listIiE

Es scheint, dass GCC die auto Linie in a std::initializer_list<const int> während Clang produziert std::initializer_list<int>. Die GCC-Version verursacht ein Problem, wenn ich sie verwende, um a zu initialisieren std::vector. Das Folgende funktioniert also unter Clang, erzeugt aber einen Compilerfehler für GCC.

// Compiles under clang but fails for GCC because l4
std::vector<int> v2 { l2 };

Wenn GCC die richtige Version produziert, scheint es vorzuschlagen, dass die verschiedenen STL-Container für diese Fälle um eine weitere Überladung des Listeninitialisierers erweitert werden sollten.

Hinweis: Dieses Verhalten scheint über mehrere Versionen von GCC (4.8, 4.9, 5.2) und Clang (3.4 und 3.6) hinweg konsistent zu sein.

  • Kannst du überprüfen std::vector<int> v2 ( l2.begin(), l2.end() ); (Es sollte funktionieren) In Anbetracht dessen sollten vielleicht Initialisiererlisten jeglichen Typs akzeptiert werden, die implizit konvertieren, selbst wenn sich die Ableitung ändert. Andererseits kann dies dazu führen, dass der Konstruktor der Initialisierungsliste andere Konstruktoren häufiger ausblendet.

    – Ben Voigt

    11. September ’15 um 4:04


  • Ja `std::vector v2 (l2.begin(), l2.end()); funktioniert tatsächlich.

    – daveraja

    11. September ’15 um 4:30

1641829079 192 const auto stdinitializer list Unterschied zwischen Clang und GCC
TC

GCC-Fehler. [dcl.spec.auto]/p7 (zitiert N4527):

Wenn eine mit einem Platzhaltertyp deklarierte Variable initialisiert wird,
[…] der abgeleitete Rückgabetyp oder Variablentyp wird vom Typ seines Initialisierers bestimmt. […] Ansonsten lass T sei der deklarierte Typ der Variablen […]. Wenn der Platzhalter der . ist auto
Typenbezeichnung, wird der abgeleitete Typ anhand der Regeln für die Ableitung von Vorlagenargumenten bestimmt. Wenn die Initialisierung
Direktlisten-Initialisierung […]. […] Andernfalls erhalten Sie P von T durch Ersetzen der Vorkommen von auto entweder mit einem neuen erfundenen Typvorlagenparameter U oder, wenn die Initialisierung ist
Kopierliste-Initialisierung, mit std::initializer_list<U>. Leiten Sie einen Wert für ab U unter Verwendung der Regeln der Deduktion von Vorlagenargumenten aus einem Funktionsaufruf (14.8.2.1), wobei P ist ein Parametertyp einer Funktionsvorlage und das entsprechende Argument ist der Initialisierer […]. Schlägt der Abzug fehl, ist die Erklärung falsch. Andernfalls wird der für die Variable oder den Rückgabetyp abgeleitete Typ durch Ersetzen des abgeleiteten U hinein P.

Also, in const auto l2 = { 1, 2, 3 };, Ableitung erfolgt wie bei der Funktionsschablone

template<class U> void meow(const std::initializer_list<U>);

den Anruf gegeben meow({1, 2, 3}).

Betrachten Sie nun den const-less-Fall auto l3 = { 1, 2, 3 }; (was GCC richtigerweise als std::initializer_list<int>). Die Ableitung erfolgt in diesem Fall wie bei der Funktionsvorlage

template<class U> void purr(std::initializer_list<U>);

den Anruf gegeben purr({1, 2, 3}).

Da die CV-Qualifizierung der obersten Ebene von Funktionsparametern ignoriert wird, sollte es offensichtlich sein, dass die beiden Ableitungen denselben Typ ergeben sollten.


[temp.deduct.call]/p1:

Die Ableitung von Vorlagenargumenten erfolgt durch Vergleichen jedes Parametertyps der Funktionsvorlage (rufen Sie es auf) P) mit dem Typ des entsprechenden Arguments des Aufrufs (call it A) wie unten beschrieben. Wenn P ist ein abhängiger Typ, der Verweise und Lebenslauf-Qualifizierer aus . entfernt
P gibt std::initializer_list<P'> […] für einige P' […] und das Argument eine nicht leere Initialisiererliste (8.5.4) ist, dann wird stattdessen für jedes Element der Initialisiererliste eine Deduktion durchgeführt, wobei
P' als Parametertyp einer Funktionsschablone und das Initialisierungselement als sein Argument.

Ableiten P' (welches ist U) gegen 1, 2, oder 3, alle Literale vom Typ int, ergibt offensichtlich int.

  • Ich sehe nicht, wie die Lebenslauf-Qualifikation auf höchstem Niveau geht es hier um das Problem ist initializer_list<const int> und nicht const initializer_list<int>

    – Ben Voigt

    11. September ’15 um 4:06

  • Tut auto l3 = { 1, 2, 3 }; NICHT denselben Vorlagenparameter ableiten (const int) in gcc?

    – Ben Voigt

    11. September ’15 um 4:10

  • Richtig auto l3 = { 1,2,3}; leitet NICHT die gleichen Vorlagenparameter ab wie const auto l2 = {1,2,3}; in gcc. Wenn ich es versuche typeid(l3).name() produziert St16initializer_listIiE.

    – daveraja

    11. September ’15 um 4:42


  • @TC Danke für die schnelle Antwort.

    – daveraja

    11. September ’15 um 4:47

const auto stdinitializer list Unterschied zwischen Clang und GCC
Shafik Yaghmour

Es gibt einen gcc-Fehlerbericht falscher automatischer Abzug aus der geschweiften Init-Liste über diesen und ähnliche Fälle und Richard Smith weist darauf hin, dass es sich um einen gcc-Fehler handelt:

Noch einfacherer Testfall:

#include <initializer_list>
const auto r = { 1, 2, 3 };
using X = decltype(r);
using X = const std::initializer_list<int>;

scheitert, weil decltype(r) wird abgeleitet als const std::initializer_list<const int> eher, als const std::initializer_list<int>.

Der Abschnitt des Entwurfs des C++-Standards wäre Abschnitt 7.1.6.4 [dcl.spec.auto] was sagt:

Wenn eine mit einem Platzhaltertyp deklarierte Variable initialisiert wird oder eine Rückgabeanweisung in einer Funktion auftritt, die mit einem Rückgabetyp deklariert wurde, der einen Platzhaltertyp enthält, wird der abgeleitete Rückgabetyp oder Variablentyp aus dem Typ seines Initialisierers bestimmt. […] Sei T der deklarierte Typ der Variablen oder der Rückgabetyp der Funktion. Wenn der Platzhalter der automatische Typspezifizierer ist, wird der abgeleitete Typ anhand der Regeln für die Ableitung von Vorlagenargumenten bestimmt. […] Andernfalls erhalten Sie P aus T, indem Sie die Vorkommen von auto entweder durch einen neuen Vorlagenparameter U des erfundenen Typs ersetzen oder, wenn der Initialisierer eine geschweifte Init-Liste ist, mit std::initializer_-list. Leiten Sie einen Wert für U ab, indem Sie die Regeln der Deduktion von Vorlagenargumenten aus einem Funktionsaufruf (14.8.2.1) verwenden, wobei P ein Parametertyp der Funktionsvorlage und der Initialisierer das entsprechende Argument ist […] [ Example:

auto x1 = { 1, 2 }; // decltype(x1) is std::initializer_list<int>
auto x2 = { 1, 2.0 }; // error: cannot deduce element type

—end example ] [ Example:

const auto &i = expr;

The type of i is the deduced type of the parameter u in the call f(expr) of the following invented function template:

template <class U> void f(const U& u);

—end example ]

  • Danke für die schnelle Antwort und den Link zum Fehlerbericht.

    – daveraja

    11. September ’15 um 4:48

.

313910cookie-checkconst auto std::initializer_list Unterschied zwischen Clang und GCC

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy