38 votes

Une syntaxe plus agréable pour définir la valeur de l'argument par défaut du constructeur par défaut.

On peut vouloir déclarer une fonction avec un argument, et spécifier que la valeur par défaut de l'argument est le résultat du constructeur par défaut du type :

void foo(a::really::long::type::name arg = a::really::long::type::name());

Existe-t-il une syntaxe plus agréable pour cela, qui n'implique pas de saisir deux fois le nom du type ? Quelque chose comme :

void foo(a::really::long::type::name arg = default);

Je réalise que je peux typedef le nom du type pour le rendre plus joli, mais je suis curieux de savoir si une telle syntaxe existe.

56voto

bolov Points 4005

Oui :

void foo(a::really::long::type::name arg = {});

Pour résumer les définitions standard suivantes :

C'est l'initialisation de la liste. Selon le type, l'initialisation de l'agrégat est effectuée ou l'objet est initialisé par valeur, ce qui implique à son tour initialisé par défaut ou initialisé par zéro.

Certains cas "limites" sont ceux où le type est une spécialisation de std::initializer_list ou lorsque le type a un std::initializer_list constructeur (il est appelé s'il n'a pas de constructeur par défaut)


Les citations standard pertinentes (dans l'ordre où nous rencontrons les définitions) :

§8.3.6 Arguments par défaut [dcl.fct.default]

1 Si une clause d'initialisation est spécifiée dans une déclaration de paramètre, ce clause d'initialisation est utilisée comme argument par défaut.

5 L'argument par défaut a les mêmes contraintes sémantiques que l'argument initialisateur dans une déclaration d'une variable du type paramètre, en utilisant la sémantique de la copie d'initialisation (8.5).

§8.5.4 Initialisation de la liste [dcl.init.list].

1 L'initialisation de liste est l'initialisation d'un objet ou d'une référence à partir d'une liste d'initialisation entre crochets. Un tel initialisateur est appelé un initialisateur liste d'initialisation, [...]. Une liste d'initialisation peut être vide. L'initialisation par liste peut se produire dans des contextes d'initialisation directe ou d'initialisation par copie ; [...] l'initialisation par liste dans un contexte d'initialisation par copie est appelée initialisation par copie de liste.

3 L'initialisation par liste d'un objet ou d'une référence de type T est définie comme suit :

  • Si T est un agrégat, l'initialisation de l'agrégat est effectuée (8.5.1)
  • Sinon, si la liste des initialisateurs n'a pas d'éléments et que T est une classe avec un constructeur par défaut, l'objet est valeur initialisée .
  • Sinon, si T est une spécialisation de std::initializer_list, un objet prvalue initializer_list est construit comme décrit ci-dessous et est utilisé pour initialiser l'objet selon les règles suivantes et est utilisé pour initialiser l'objet selon les règles de l'initialisation d'un objet d'une classe du même type (8.5).
  • Sinon, si T est un type de classe, les constructeurs sont pris en compte. Les constructeurs applicables sont énumérés et le meilleur est choisi par résolution de surcharge (13.3, 13.3.1.7) [...]
  • ...
  • Sinon, si la liste des initialisateurs n'a pas d'éléments, l'objet est valeur initialisée .

§ 8.5 Initialisateurs [dcl.init].

8 To valeur-initialisation un objet de type T signifie :

  • si T est un type de classe (éventuellement qualifié de cv) (clause 9) sans constructeur par défaut (12.1) ou avec un constructeur par défaut qui est fourni par l'utilisateur ou supprimé, alors l'objet est initialisé par défaut ;
  • si T est un type de classe (éventuellement qualifiée cv) sans constructeur par défaut fourni par l'utilisateur ou supprimé, alors l'objet est zéro initialisé et les contraintes sémantiques pour l'initialisation par défaut sont vérifiées, et si T possède un constructeur par défaut non trivial, l'objet est initialisé par défaut ;
  • si T est un type de tableau, alors chaque élément est valeur initialisée ;
  • sinon, l'objet est zéro initialisé

7 To Initialisation par défaut un objet de type T signifie :

  • si T est un type de classe (éventuellement qualifié de cv) (clause 9), le constructeur par défaut (12.1) de T est appelé (et l'initialisation est mal formée si T n'a pas de constructeur par défaut ou de résolution de surcharge. (13.3) aboutit à une ambiguïté ou à une fonction qui est supprimée ou inaccessible dans le contexte de l'initialisation) ;
  • si T est un type de tableau, chaque élément est initialisé par défaut ;
  • sinon, aucune initialisation n'est effectuée.

6 To zéro-initialisation un objet ou une référence de type T signifie :

  • si T est un type scalaire (3.9), l'objet est initialisé à la valeur obtenue en convertissant le littéral entier 0 (zéro) en T ;
  • si T est un type de classe non unitaire (éventuellement qualifié par cv), chaque membre de données non statique et chaque sous-objet de classe de base est initialisé à zéro et le remplissage est initialisé à zéro bit ;
  • si T est un type union (éventuellement qualifié cv), le premier membre de données nommé non statique de l'objet est initialisé à zéro et le remplissage est initialisé à zéro bit ;
  • si T est un type tableau, chaque élément est initialisé à zéro ;
  • si T est un type de référence, aucune initialisation n'est effectuée.

§13.3.1.7 Initialisation par list-initialisation [over.match.list]

1 Lorsque les objets de la catégorie de classe non agrégée T sont initialisés par liste. (8.5.4), la résolution de surcharge sélectionne le constructeur en deux phases :

  • Initialement, les fonctions candidates sont les suivantes Constructeurs de liste d'initialisation (8.5.4) de la classe T et la liste d'arguments consiste en la liste des initialisateurs comme un seul argument.
  • Si aucun constructeur viable de la liste d'initialisation n'est trouvé, la résolution de surcharge est à nouveau effectuée, où les fonctions candidates sont toutes les constructeurs de la classe T et la liste d'arguments est constituée des éléments éléments de la liste d'initialisation.

Si la liste des initialisateurs n'a pas d'éléments et que T a un défaut par défaut, la première phase est omise. [...]

3voto

Kaz Points 18072

Une approche piétonne est possible, si vous contrôlez la classe de arg . Utilisez un constructeur de conversion surchargé pour un fichier enum :

// Define this enum, and then write constructors which take dfl
enum dfl { dflval };

class a_really_long_type_name {
public:
  a_really_long_type_name(dfl arg = dflval);
};

Maintenant, foo peut l'être :

void foo(a_really_long_type_name arg = dflval);

Si vous pouvez appliquer cela, un avantage est la portabilité ; cela devrait fonctionner correctement dans un compilateur C++ vieux de vingt-cinq ans.

Les classes multiples peuvent toutes partager cette dfl enum et son dflval -zéro aromatisé ; c'est comme avoir un nouveau mot-clé.

Parce qu'un enum est un type distinct, cela n'interfère pas avec les surcharges de constructeurs pour les types entiers ou les caractères, etc.

L'inconvénient est de l'intégrer dans certaines classes qui ont déjà une construction par défaut fournie par le biais de l'argument defaulting, ce qui conduit à la duplication du code du constructeur.

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