Jaka jest różnica między 'typedef' a 'using' w C++11?

Wiem, że w C++11 możemy teraz używać using do pisania aliasów typu, np. typedef s:

typedef int MyInt;

Jest, z tego co rozumiem, odpowiednikiem:

using MyInt = int;

I ta nowa składnia powstała z wysiłku, aby mieć sposób na wyrażenie "szablon typedef":

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

Ale czy przy pierwszych dwóch przykładach nie szablonowych są jakieś inne subtelne różnice w standardzie? Na przykład, typedefrobią aliasing w "słaby" sposób. Oznacza to, że nie tworzy nowego typu, ale tylko nową nazwę (konwersje są niejawne między tymi nazwami).

Jest to to samo z using czy generuje nowy typ? Czy są jakieś różnice?

Author: user3840170, 2012-05-25

7 answers

wszystkie standardowe odniesienia poniżej odnoszą się do N4659: Marzec 2017 Post-Kona working draft/C++17 dis.


Deklaracje Typedef mogą, podczas gdy deklaracje aliasów nie mogą być używane jako instrukcje inicjalizacji

Ale, z dwoma pierwszymi przykładami nie szablonowymi, są są jakieś inne subtelne różnice w standardzie?

  • różnice w semantyce : brak.
  • różnice w dozwolonych kontekstach : niektóre(1).

(1) oprócz przykładów szablonów aliasów , o których już wspomniano w oryginalnym poście.

Ta sama semantyka

Zgodnie z [dcl.typedef]/2 [Wyciąg, podkreślenie moje]

[dcl.typedef] / 2 A typedef-name może być również wprowadzony przez alias-deklaracja. Identyfikator po using słowo kluczowe staje się typedef-name i opcjonalny atrybut-specifier-seq po identyfikatorze przypisany do typedef-name. taki typedef-name ma taką samą semantykę, jakby została wprowadzona przez specyfikator typedef. [...]

A typedef-name wprowadzony przez alias-declaration ma tę samą semantykę tak, jakby został wprowadzony przez typedef deklaracja.

Subtelne różnica w dozwolonych kontekstach

Nie oznacza to jednak , że te dwie odmiany mają takie same ograniczenia w odniesieniu do kontekstów , w których mogą być używane. I rzeczywiście, choć sprawa narożna, a deklaracja typedef jest INIT-oświadczenie i mogą być używane w kontekstach, które pozwalają na inicjalizację instrukcji

// 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

for(typedef struct { int x; int y;} P;
//  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ init-statement
    auto [x, y] : {P{1, 1}, {1, 2}, {3, 5}}) { (void)x; (void)y; }

alias-deklaracja jest Nie an INIT-statement, a zatem nie może być używane w kontekstach, które pozwalają na inicjalizację instrukcji

// 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
 53
Author: dfrib,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-12-07 17:59:25

Są równoważne ze standardem (7.1.3.2):

Typedef-name może być również wprowadzony przez alias-declaration. Na identifier following the using keyword becomes a typedef-name and the opcjonalny atrybut-specifier-seq po pojawieniu się identyfikatora do tego imienia. ma tę samą semantykę, jakby była wprowadzony przez typedef specifier. w szczególności nie definiuje nowego typu i nie pojawia się w type-id.

 595
Author: Jesse Good,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-05-25 03:16:42

Są w dużej mierze takie same, z tym że:

Deklaracja aliasu jest zgodna z szablonami, natomiast C styl typedef nie jest.

 256
Author: Zhongming Qu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-23 14:54:25

Składnia z użyciem składni ma przewagę, gdy jest używana w szablonach. Jeśli potrzebujesz abstrakcji typu, ale także musisz zachować parametr szablonu, aby mógł być określony w przyszłości. Powinieneś napisać coś takiego.

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; }

Ale użycie składni upraszcza ten przypadek użycia.

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

my_type<int> variable;
template <typename U> struct baz { my_type<U> _var_member; }
 205
Author: 4xy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-24 12:51:25

Są zasadniczo takie same, ale using Dostarcza alias templates, co jest całkiem przydatne. Jeden dobry przykład, który mogłem znaleźć jest następujący:

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

Więc możemy użyć std::add_const_t<T> zamiast typename std::add_const<T>::type

 23
Author: Validus Oculus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-31 08:20:30

Wiem, że oryginalny plakat ma świetną odpowiedź, ale dla każdego, kto natknie się na ten wątek, jak mam, jest ważna uwaga z propozycja , która moim zdaniem dodaje coś wartościowego do dyskusji tutaj, szczególnie do obaw w komentarzach o tym, czy słowo kluczowe typedef zostanie oznaczone jako przestarzałe w przyszłości lub usunięte za bycie zbędnym/starym:{[15]]}

Zasugerowano (ponowne) użycie słowa kluczowego typedef ... aby wprowadzić szablon aliasy:

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

Ta notacja ma tę zaletę, że używa się już znanego słowa kluczowego do wprowadzenia aliasu typu. Jednak wyświetla również kilka dezawantacji [sic], wśród których mylenie użycia słowa kluczowego znanego z wprowadzenia aliasu dla nazwy typu w kontekście, w którym alias nie oznacza typu, ale szablon; Vec jest , a nie aliasem dla typu i nie powinien być brany za typedef-name. Nazwa {[3] } jest nazwą rodziny std::vector<•, MyAllocator<•> > - gdzie kula jest symbol zastępczy dla nazwy typu.W związku z tym nie proponujemy składni "typedef".Z drugiej strony zdanie

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

Można odczytać/zinterpretować jako: od teraz będę używać Vec<T> jako synonimu dla std::vector<T, MyAllocator<T> >. Przy takim odczycie Nowa składnia aliasingu wydaje się w miarę logiczna.

Dla mnie oznacza to ciągłe wsparcie dla typedef słowa kluczowego w C++, ponieważ może to jeszcze uczynić kod bardziej czytelnym i zrozumiałym.

Aktualizacja using słowo kluczowe było przeznaczone specjalnie dla szablonów i (jak zaznaczono w zaakceptowanej odpowiedzi), gdy pracujesz z nie-szablonami using i {[2] } są mechanicznie identyczne, więc wybór należy całkowicie do programisty ze względu na czytelność i komunikowanie intencji.

 13
Author: RoboticForest,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-06-12 02:59:59

Oba słowa kluczowe są równoważne, ale jest kilka zastrzeżeń. Jednym z nich jest to, że deklarowanie wskaźnika funkcji za pomocą using T = int (*)(int, int); jest jaśniejsze niż za pomocą typedef int (*T)(int, int);. Po drugie, forma aliasu szablonu nie jest możliwa z typedef. Po trzecie, ujawnienie C API wymagałoby typedef w nagłówkach publicznych.

 5
Author: marski,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-12-02 22:23:42