Construire un vecteur avec un intervalle d'éléments

Un erreur que je n'arrive pas comprendre

a marqué ce sujet comme résolu.

Bonsoir à tous,

Je suis victime d'un problème dont j'ai étrangement de la peine à comprendre la cause.

Imaginons que je veuille construire un std::vector avec un intervalle d'éléments donné par deux itérateurs, sous la forme :
template<class InputIt> std::vector(InputIt first, InputIt last).

Le code suivant, par exemple, fonctionnera parfaitement :

1
2
std::string s = "abcd";
std::vector<char> v(s.cbegin(), s.cend());

En revanche, utiliser des std::istream_iterator semble poser problème, et pour une raison que je ne saisis pas. Prenons ce code :

1
2
3
4
5
template<class T>
using isIt = std::istream_iterator<T>;
// ...
std::istringstream flux("12 35 276 375 34");
std::vector<int> v(isIt<int>(flux), isIt<int>());

Aucun problème à la compilation. Cependant, l'objet v de type std::vector ne semble pas avoir été créé. Il suffit d'appeler une de ses fonctions membres pour obtenir une erreur du type :

1
error: request for member 'size' in 'v', which is of non-class type 'std::vector<int>(isIt<int>, isIt<int> (*)())

Donc visiblement, je n'ai pas construit un std::vector, mais quelque chose d'autre, et malgré le message d'erreur et mes diverses recherches sur Internet, je n'ai pas pu comprendre ce que c'est réellement.

J'ai d'ailleurs remarqué que l'utilisation des accolades au lieu des parenthèses appelait correctement le constructeur et ne causait ainsi pas de problème. De même, initialiser le vecteur avec assign fonctionne parfaitement.

1
2
std::vector<int> v{ isIt<int>(flux), isIt<int>() }; // fonctionne
v.assign(isIt<int>(flux), isIt<int>()); // fonctionne également

Je vous demande ainsi : quel peu bien être cet objet créé qui n'est pas un std::vector ? Et quel en est la cause d'un tel comportement ?

Merci d'avance pour vos réponses.

Qu'est ce que ça donne avec ceci?

1
std::vector<int> v(isIt<int>(flux), isIt<int>{});

isIt<int>() est équivalent à isIt<int>(*)(void) ce qui rend v un pointeur de fonction. :D

PS: active les warnings de ton compilateur pour repérer les erreurs plus rapidement. Par exemple, -Wall -Wextra -Weverything -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-global-constructors pour clang et -Wall -Wextra -pedantic pour gcc. (/W3 ou /W4 pour VC++)

+2 -0

Merci pour la réponse, mais je ne suis pas sûr d'avoir bien compris la chose.

Un des constructeurs de std::vector est-il bien appelé ou non ?

Et pourquoi l'utilisation des parenthèses fait de v un pointeur de fonction, alors qu'avec les accolades ce problème n'a plus lieu ? (J'avais pensé que les accolades appelaient le constructeur demandant un std::initializer_list, mais cela semble impossible vu que le type std::istream_iterator<int> n'est pas convertissable en ìnt…)

EDIT: J'utilise GCC et je compile déjà avec ces options de warnings, qu'apportent-elles ici ?

+0 -0

Comme je comprends l'erreur, en fait c'est une prédéclaration de fonction. ON pourrait essayer de l'appeler pour voir, ça devrait faire une erreur de link.

Et pourquoi l'utilisation des parenthèses fait de v un pointeur de fonction, alors qu'avec les accolades ce problème n'a plus lieu ? (J'avais pensé que les accolades appelaient le constructeur demandant un std::initializer_list, mais cela semble impossible vu que le type std::istream_iterator<int> n'est pas convertissable en ìnt…)

Depuis C++11 (ou 14, je ne sais plus), les accolades ne servent plus uniquement pour des std::initializer_list. En fait on peut les utiliser à peu près partout pour construire n'importe quoi maintenant, et elles sont même largement conseillées si on en croit certains sites.

JE crois que c'est justement pour lever ce genre d'ambiguïtés qu'on a introduit cette notation.

J'imagine que c'est vu comme une ambiguïté essentiellement à cause du fait qu'on soit dans un contexte de template, autrement le passage du paramètre flux aurait dû suffire.

+2 -0

Donc si j'ai bien compris, cette manière bien précise de définir un std::vector déclare en réalité une fonction. Toutefois, je n'ai toujours pas saisi exactement pourquoi cette syntaxe produit ce comportement.

Bref, l'utilisation d'accolades permet donc de "forcer" l'appel du constructeur (vu qu'une fonction ne peut pas être déclarée avec des accolades). En gros, il faudrait penser à mettre des accolades dans ce genre de situations ambiguë.

Merci pour vos réponses. ;)

le problème vient des parenthèses "vides". en C, pour déclarer une fonction qui ne prend rien en argument et renvoi un int, par exemple, on aurait fait:

1
int f(void); // en C

En C++, il n'y a pas d'obligation de mettre de "void". Du coup:

1
2
int f(void); // à la C
int g(); // deuxième option

Mais en C++, on peut aussi "créer" un int en faisant int(). Le problème est que ça rentre en conflit avec une déclaration d'une fonction qui prend void. Et quand le contexte n'est pas clair, le compilateur considère que c'est une déclaration de fonction (ou que c'est un pointeur de fonction, dans d'autres).

Ainsi:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
#include <string>
#include <type_traits>
#include <cassert>
using namespace std;
struct A
{
    A(string, string){}
};

int main()
{
    A f(string(), string());
    bool typeCheck = std::is_same<decltype(f), A(string(*)(void), string(*)(void))>::value;
    assert(typeCheck); // typeCheck == true. Et donc f n'est un objet de type A
}
+1 -0
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte