71 votes

C ++11 liste d’initialiseurs échoue - mais seulement sur les listes de longueur 2

J'ai dégotté un obscur journalisation bug sur le fait que l'initialiseur de listes de longueur 2 semble être un cas spécial! Comment est-ce possible?

Le code a été compilé avec Apple LLVM version 5.1 (clang-503.0.40), à l'aide de CXXFLAGS=-std=c++11 -stdlib=libc++.

#include <stdio.h>

#include <string>
#include <vector>

using namespace std;

typedef vector<string> Strings;

void print(string const& s) {
    printf(s.c_str());
    printf("\n");
}

void print(Strings const& ss, string const& name) {
    print("Test " + name);
    print("Number of strings: " + to_string(ss.size()));
    for (auto& s: ss) {
        auto t = "length = " + to_string(s.size()) + ": " + s;
        print(t);
    }
    print("\n");
}

void test() {
    Strings a{{"hello"}};                  print(a, "a");
    Strings b{{"hello", "there"}};         print(b, "b");
    Strings c{{"hello", "there", "kids"}}; print(c, "c");

    Strings A{"hello"};                    print(A, "A");
    Strings B{"hello", "there"};           print(B, "B");
    Strings C{"hello", "there", "kids"};   print(C, "C");
}

int main() {
    test();
}

Sortie:

Test a
Number of strings: 1
length = 5: hello

Test b
Number of strings: 1
length = 8: hello

Test c
Number of strings: 3
length = 5: hello
length = 5: there
length = 4: kids

Test A
Number of strings: 1
length = 5: hello

Test B
Number of strings: 2
length = 5: hello
length = 5: there

Test C
Number of strings: 3
length = 5: hello
length = 5: there
length = 4: kids

Je dois également ajouter que la longueur de la chaîne bidon dans l'essai b semble être de durée indéterminée, il est toujours plus grande que la première initialisation de la chaîne, mais a varié de plus que la longueur de la première chaîne à la somme des longueurs des deux chaînes dans l'initialiseur.

76voto

Filip Roséen - refp Points 24995

Introduction

Imaginez la déclaration suivante, et l'utilisation:

struct A {
  A (std::initializer_list<std::string>);
};

A {{"a"          }}; // (A), initialization of 1 string
A {{"a", "b"     }}; // (B), initialization of 1 string << !!
A {{"a", "b", "c"}}; // (C), initialization of 3 strings

Dans (Un) et (C), chaque c chaîne de style est à l'origine de l'initialisation d'un (1) std::string, mais, comme vous l'avez dit dans votre question, (B) diffère.

Le compilateur voit qu'il est possible de construire un std::string à l'aide d'un début et de fin d'itérateur, et lors de l'analyse de l'énoncé (B) il préfère tels construire plus à l'aide d' "a" et "b" individuels initialiseurs de deux éléments.

A { std::string { "a", "b" } }; // the compiler's interpretation of (B)


Remarque: Le type d' "a" et "b" est char const[2], un type qui peut implicitement se désintégrer en char const*, un pointeur de type qui est adapté à agir comme un itérateur qui dénote soit de commencer ou de fin lors de la création d'un std::string.. mais il faut être prudent: nous sommes à l'origine non définie-comportement puisqu'il n'est pas (garanti) relation entre les deux pointeurs sur l'invocation, a déclaré le constructeur.


Explication

Lorsque vous faites appel à un constructeur prenant un std::initializer_list à l'aide d'accolades doubles {{ a, b, ... }}, il y a deux interprétations possibles:

  1. L'extérieur des accolades consulter le constructeur lui-même, à l'intérieur des accolades indique les éléments à prendre part dans le std::initializer_list, ou:

  2. L'extérieur des accolades reportez-vous à la std::initializer_list, tandis que l'intérieur des accolades indique l'initialisation d'un élément à l'intérieur d'elle.

Il est préféré faire 2) chaque fois que cela est possible, et depuis std::string a un constructeur prenant deux itérateurs, c'est celui qui est appelé lorsque vous avez std::vector<std::string> {{ "hello", "there" }}.

Autre exemple:

std::vector<std::string> {{"this", "is"}, {"stackoverflow"}}.size (); // yields 2

Solution

Ne pas utiliser de doubles accolades pour une telle initialisation.

20voto

chris Points 28950

Tout d'abord, c'est un comportement indéterminé, sauf si je suis absent quelque chose d'évident. Maintenant laissez-moi vous expliquer. Le vecteur est construit à partir d'un initialiseur de liste de chaînes de caractères. Cependant, cette liste ne contient une chaîne de caractères. Cette chaîne est formée par l'inner {"Hello", "there"}. Comment? Avec l'itérateur constructeur. Essentiellement, for (auto it = "Hello"; it != "there"; ++it) est de former une chaîne de caractères contenant Hello\0.

Pour un exemple simple, voir ici. Alors que l'AC est une raison suffisante, il semble que la deuxième littérale est placé juste après le premier dans la mémoire. Comme un bonus, ne "Hello", "Hello" et vous aurez probablement obtenir une chaîne de caractères de longueur 0. Si vous ne comprenez pas quelque chose ici, je recommande la lecture de Filip excellente réponse.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X