Quelle est la différence entre «typedef» et «using» en C ++ 11?

905

Je sais qu'en C ++ 11, nous pouvons maintenant utiliser usingpour écrire un alias de type, comme typedefs:

typedef int MyInt;

D'après ce que je comprends, équivaut à:

using MyInt = int;

Et cette nouvelle syntaxe est née de l'effort pour trouver un moyen d'exprimer " template typedef":

template< class T > using MyType = AnotherType< T, MyAllocatorType >;

Mais, avec les deux premiers exemples non modèles, y a-t-il d'autres différences subtiles dans la norme? Par exemple, typedefeffectuez un aliasing de manière "faible". C'est-à-dire qu'il ne crée pas un nouveau type mais seulement un nouveau nom (les conversions sont implicites entre ces noms).

Est-ce la même chose usingou génère-t-il un nouveau type? Y a-t-il des différences?

Klaim
la source
215
Personnellement, je préfère la nouvelle syntaxe car elle est beaucoup plus similaire à l'affectation de variable régulière, améliorant la lisibilité. Par exemple, préférez-vous typedef void (&MyFunc)(int,int);ou using MyFunc = void(int,int);?
Matthieu M.
13
Je suis entièrement d'accord, j'utilise uniquement la nouvelle syntaxe maintenant. C'est pourquoi je demandais, pour être sûr qu'il n'y a vraiment aucune différence.
Klaim
80
@MatthieuM. ces deux-là sont différents. Ça devrait être typedef void MyFunc(int,int);(ce qui n'a pas l'air aussi mauvais), ouusing MyFunc = void(&)(int,int);
R. Martinho Fernandes
5
@ R.MartinhoFernandes pourquoi avez-vous besoin (&) de using MyFunc = void(&)(int,int);? cela signifie-t-il MyFuncune référence à une fonction? que faire si vous omettez le & ?
Rich
7
Oui, c'est une référence de fonction. C'est équivalent à typedef void (&MyFunc)(int,int);. Si vous omettez le &c'est équivalent àtypedef void MyFunc(int,int);
R. Martinho Fernandes

Réponses:

585

Ils sont équivalents, du standard (c'est moi qui souligne) (7.1.3.2):

Un nom de typedef peut également être introduit par une déclaration d'alias. L'identifiant suivant le mot-clé using devient un nom de typedef et l'attribut-specifier-seq facultatif suivant l'identifiant appartient à ce nom de typedef. Il a la même sémantique que s'il avait été introduit par le spécificateur typedef. En particulier, il ne définit pas de nouveau type et il ne doit pas apparaître dans l'identifiant de type.

Jesse Good
la source
24
D'après la réponse, le usingmot clé semble être un surensemble de typedef. Alors, sera typdefobsolète à l'avenir?
iammilind
49
La dépréciation n'indique pas nécessairement l' intention de supprimer - elle représente simplement une recommandation très forte de préférer un autre moyen.
Iron Savior
18
Mais je me demande alors pourquoi ils n'ont pas simplement laissé le typedef être modelé. Je me souviens d'avoir lu quelque part qu'ils avaient introduit la usingsyntaxe précisément parce que la typedefsémantique ne fonctionnait pas bien avec les modèles. Ce qui contredit en quelque sorte le fait que l' usingon ait exactement la même sémantique.
celtschk
12
@celtschk: La raison est mentionnée dans la proposition n1489. Un alias de modèle n'est pas un alias pour un type, mais un alias pour un groupe de modèles. Faire une distinction entre typedefle ressenti un besoin de nouvelle syntaxe. En outre, gardez à l'esprit que la question de l'OP concerne la différence entre les versions non modèles.
Jesse Good
4
Alors pourquoi cette redondance a-t-elle été introduite? 2 syntaxes dans le même but. Et je ne vois typdefjamais être déprécié.
Benoît
237

Ils sont en grande partie les mêmes, sauf que:

La déclaration d'alias est compatible avec les modèles, contrairement au typedef de style C.

Zhongming Qu
la source
29
Particulièrement friands de la simplicité de la réponse et soulignant l'origine de la composition.
g24l
1
@ g24l vous voulez dire typedef ... probablement
Marc.2377
Quelle est la différence entre C et C ++ typedefsi je peux demander?
McSinyx
196

La syntaxe d' utilisation présente un avantage lorsqu'elle est utilisée dans des modèles. Si vous avez besoin de l'abstraction de type, mais devez également conserver le paramètre de modèle pour pouvoir être spécifié à l'avenir. Vous devriez écrire quelque chose comme ça.

template <typename T> struct whatever {};

template <typename T> struct rebind
{
  typedef whatever<T> type; // to make it possible to substitue the whatever in future.
};

rebind<int>::type variable;

template <typename U> struct bar { typename rebind<U>::type _var_member; }

Mais l' utilisation de la syntaxe simplifie ce cas d'utilisation.

template <typename T> using my_type = whatever<T>;

my_type<int> variable;
template <typename U> struct baz { my_type<U> _var_member; }
4xy
la source
35
Je l'ai déjà souligné dans la question. Ma question est de savoir si vous n'utilisez pas de modèle, y a-t-il une différence avec typedef. Comme, par exemple, lorsque vous utilisez 'Foo foo {init_value};' au lieu de «Foo foo (init_value)», les deux sont censés faire la même chose mais ne respectent pas exactement les mêmes règles. Je me demandais donc s'il y avait une différence cachée similaire avec l'utilisation de / typedef.
Klaim
24

Ils sont essentiellement les mêmes mais usingfournissent alias templatesce qui est assez utile. Un bon exemple que j'ai pu trouver est le suivant:

namespace std {
 template<typename T> using add_const_t = typename add_const<T>::type;
}

Donc, nous pouvons utiliser std::add_const_t<T>au lieu detypename std::add_const<T>::type

Validus Oculus
la source
Pour autant que je sache, c'est un comportement indéfini d'ajouter quoi que ce soit à l'intérieur de l'espace de noms std
someonewithpc
5
@someonewithpc Je n'ajoutais rien, cela existe déjà, je montrais juste un exemple d'utilisation du nom de type. Veuillez consulter en.cppreference.com/w/cpp/types/add_cv
Oculus
9

Je sais que l'affiche originale a une excellente réponse, mais pour quiconque tombe sur ce fil comme moi, il y a une note importante de la proposition qui, je pense, ajoute quelque chose de valeur à la discussion ici, en particulier aux préoccupations dans les commentaires sur la question de savoir si le typedefmot-clé est va être marqué comme obsolète à l'avenir, ou supprimé pour être redondant / ancien:

Il a été suggéré de (ré) utiliser le mot-clé typedef ... pour introduire des alias de modèle:

template<class T>
  typedef std::vector<T, MyAllocator<T> > Vec;

Cette notation a l'avantage d'utiliser un mot-clé déjà connu pour introduire un alias de type. Cependant, il présente également plusieurs inconvénients [sic] parmi lesquels la confusion de l'utilisation d'un mot-clé connu pour introduire un alias pour un nom de type dans un contexte où l'alias ne désigne pas un type, mais un modèle; Vecn'est pas un alias pour un type et ne doit pas être pris pour un nom de type. Le nom Vecest un nom pour la famille std::vector<•, MyAllocator<•> >- où la puce est un espace réservé pour un nom de type. Par conséquent, nous ne proposons pas la syntaxe "typedef". Par contre, la phrase

template<class T>
  using Vec = std::vector<T, MyAllocator<T> >;

peut être lu / interprété comme: à partir de maintenant, je vais utiliser Vec<T>comme synonyme destd::vector<T, MyAllocator<T> > . Avec cette lecture, la nouvelle syntaxe pour l'aliasing semble raisonnablement logique.

Pour moi, cela implique une prise en charge continue du typedefmot clé en C ++ car il peut encore rendre le code plus lisible et compréhensible .

La mise à jour du usingmot - clé était spécifiquement pour les modèles et (comme cela a été souligné dans la réponse acceptée) lorsque vous travaillez avec des non-modèles usinget typedefsont mécaniquement identiques, le choix appartient donc entièrement au programmeur pour des raisons de lisibilité et de communication d'intention. .

RoboticForest
la source
2

Les deux mots clés sont équivalents, mais il y a quelques mises en garde. La première est que déclarer un pointeur de fonction avec using T = int (*)(int, int);est plus clair qu'avec typedef int (*T)(int, int);. Deuxièmement, la forme d'alias de modèle n'est pas possible avec typedef. Troisièmement, exposer l'API C nécessiterait des typedefen-têtes publics.

marski
la source
0

Les déclarations typedef peuvent, contrairement aux déclarations d'alias, être utilisées comme instructions d'initialisation

Mais, avec les deux premiers exemples non modèles, y a-t-il d'autres différences subtiles dans la norme?

Bien qu'il s'agisse d'un cas de coin, une déclaration typedef est une instruction init et peut donc être utilisée dans des contextes qui permettent des instructions d'initialisation

// C++11 (C++03) (init. statement in for loop iteration statements).
for(typedef int Foo; Foo{} != 0;) {}

// C++17 (if and switch initialization statements).
if (typedef int Foo; true) { (void)Foo{}; }
//  ^^^^^^^^^^^^^^^ init-statement

switch(typedef int Foo; 0) { case 0: (void)Foo{}; }
//     ^^^^^^^^^^^^^^^ init-statement

// C++20 (range-based for loop initialization statements).
std::vector<int> v{1, 2, 3};
for(typedef int Foo; Foo f : v) { (void)f; }
//  ^^^^^^^^^^^^^^^ init-statement

alors qu'une alias-declaration n'est pas une instruction init et ne peut donc pas être utilisée dans des contextes qui permettent des instructions d'initialisation

// C++ 11.
for(using Foo = int; Foo{} != 0;) {}
//  ^^^^^^^^^^^^^^^ error: expected expression

// C++17 (initialization expressions in switch and if statements).
if (using Foo = int; true) { (void)Foo{}; }
//  ^^^^^^^^^^^^^^^ error: expected expression

switch(using Foo = int; 0) { case 0: (void)Foo{}; }
//     ^^^^^^^^^^^^^^^ error: expected expression

// C++20 (range-based for loop initialization statements).
std::vector<int> v{1, 2, 3};
for(using Foo = int; Foo f : v) { (void)f; }
//  ^^^^^^^^^^^^^^^ error: expected expression
dfri
la source