Le constructeur de la plage std :: vector peut-il invoquer des conversions explicites?

14

Le programme suivant est-il bien formé?

#include <vector>
struct A {
    explicit A(int) {}
};
int main() {
    std::vector<int> vi = {1, 2, 3, 4, 5};
    std::vector<A> va(vi.begin(), vi.end());
}

Selon C ++ 17 [sequence.reqmts], l'exigence de

X u(i, j);

Xest un conteneur de séquence, est:

Tdoit être EmplaceConstructibleà Xpartir de *i.

Cependant, au paragraphe précédent, il est indiqué que:

iet jdésignent des itérateurs satisfaisant aux exigences des itérateurs d'entrée et font référence à des éléments implicitement convertibles en value_type,

Ainsi, il me semble que les deux exigences devraient être remplies: le type de valeur de la plage doit être implicitement convertible en type de valeur du conteneur, et EmplaceConstructible doit être satisfait (ce qui signifie que l'allocateur doit pouvoir effectuer l'initialisation requise) . Puisqu'il intn'est pas implicitement convertible en A, ce programme devrait être mal formé.

Cependant, étonnamment, il semble compiler sous GCC .

Brian
la source
(Pour mémoire, ce n'est pas seulement gcc: godbolt.org/z/ULeRDw )
Max Langhof
Dans ce cas, aucune conversion implicite n'est requise, car le constructeur explicite correspond déjà au type. Je pense que la description est déroutante, mais la construction explicite est toujours meilleure que la conversion implicite avant la construction.
JHBonarius

Réponses:

2

Il suffit que les conteneurs de séquences prennent en charge la construction à partir d'itérateurs qui satisfont aux critères de convertibilité implicite.

Cela ne permet pas en soi d'interdire aux conteneurs de séquence de soutenir cette construction à partir d'itérateurs qui ne satisfont pas à ces critères pour autant que je sache 1 . Il y a une règle explicite à ce sujet:

Si le constructeur ... est appelé avec un type InputIterator qui ne se qualifie pas en tant qu'itérateur d'entrée , le constructeur ne doit pas participer à la résolution de surcharge.

On ne sait pas exactement ce que "qualifier d'itérateur d'entrée" signifie exactement dans le contexte. Est-ce un moyen informel d'exprimer Cpp17InputIterator ou tente-t-il de se référer aux exigences de i et j? Je ne sais pas. Qu'elle soit autorisée ou non, la norme n'a pas d'exigence stricte pour la détecter:

[container.requirements.general]

Le comportement de certaines fonctions de membre de conteneur et guides de déduction dépend de la qualification des types d'itérateurs d'entrée ou d'allocateurs. La mesure dans laquelle une implémentation détermine qu'un type ne peut pas être un itérateur d'entrée n'est pas spécifiée, sauf que, au minimum, les types intégraux ne doivent pas être qualifiés d'itérateurs d'entrée. ...

Avec l'interprétation que tout Cpp17InputIterator "se qualifie comme un itérateur d'entrée", le programme d'exemple n'aurait pas besoin d'être mal formé. Mais il n'est pas non plus garanti d'être bien formé.

1 Dans ce cas, il peut être considéré comme un problème de qualité de mise en œuvre d'avertir lorsque vous vous y fiez. En revanche, cette limitation aux conversions implicites peut être considérée comme un défaut .


PS Cela compile sans avertissements dans Clang (avec libc ++) et Msvc également.

PPS Cette formulation semble avoir été ajoutée en C ++ 11 (ce qui est naturel, tout comme les constructeurs explicites ont également été introduits).

eerorika
la source
1
Cela dépend vraiment de ce que "ne peut pas qualifier d'itérateur d'entrée" . Contrairement à ce qui précède , il ne le dit pas réellement Cpp17InputIterator, il n'est donc pas clair pour moi si "et faire référence à des éléments implicitement convertibles en value_type" est inclus dans "itérateur d'entrée". Si c'est le cas, le constructeur ne doit pas participer à la résolution de surcharge et le programme doit être mal formé.
Max Langhof
1
Ainsi, chaque classe de bibliothèque standard est autorisée à avoir des constructeurs supplémentaires sans émettre de diagnostic lorsque ces constructeurs supplémentaires sont utilisés? Cela me semble intuitivement mal ...
Brian
@Brian Je ne sais pas si ses "constructeurs supplémentaires", mais peut-être plus "des implémentations spécifiques de constructeurs qui permettent plus d'espace". Vérifier chaque entrée pourrait avoir un impact significatif sur les performances, donc je ne sais pas si ce serait la voie à suivre ...
JHBonarius
@Brian Ce serait certainement une mauvaise idée même si elle n'est pas explicitement refusée. Dans ce cas, nous examinons uniquement si un constructeur requis est autorisé à prendre en charge les types d'itérateurs qu'il n'est pas tenu de prendre en charge. Dans ce cas, il existe une exigence explicite de "ne pas participer", comme l'a souligné Max, ce qui ne le permettrait certainement pas. Mais on ne sait pas vraiment ce que "qualifier d'itérateur d'entrée" signifie exactement dans le contexte. Est-ce une manière informelle d'exprimer Cpp17InputIteratorou tente-t-elle de se référer aux exigences de iet j? Je ne sais pas.
eerorika
2
1) En C ++, nous fixons le standard bas. . 2) Les constructeurs sont des fonctions membres non virtuelles . 3) Voir LWG 3297 ; cependant, je ne suis pas particulièrement convaincu que nous devrions supprimer l'exigence de conversion implicite.
TC