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

905

Wiem, że w C ++ 11 możemy teraz używać usingdo pisania aliasu typu, takiego jak typedefs:

typedef int MyInt;

Z tego, co rozumiem, jest równoważne z:

using MyInt = int;

Ta nowa składnia powstała w wyniku wysiłku, aby wyrazić „ template typedef”:

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

Ale czy w przypadku dwóch pierwszych przykładów innych niż szablony istnieją inne subtelne różnice w standardzie? Na przykład typedefaliasing w „słaby” sposób. Oznacza to, że nie tworzy nowego typu, a jedynie nową nazwę (konwersje są niejawne między tymi nazwami).

Czy jest tak samo z usingczy generuje nowy typ? Czy są jakieś różnice?

Klaim
źródło
215
Osobiście wolę nową składnię, ponieważ jest ona znacznie bardziej podobna do zwykłego przypisywania zmiennych, co poprawia czytelność. Na przykład wolisz typedef void (&MyFunc)(int,int);czy using MyFunc = void(int,int);?
Matthieu M.,
13
W pełni się zgadzam, teraz używam nowej składni. Właśnie dlatego pytałem, aby mieć pewność, że tak naprawdę nie ma różnicy.
Klaim
80
@MatthieuM. te dwa są różne przy okazji. Powinien to być typedef void MyFunc(int,int);(co tak naprawdę nie wygląda tak źle), lubusing MyFunc = void(&)(int,int);
R. Martinho Fernandes
5
@ R.MartinhoFernandes, dlaczego potrzebujesz (i) w using MyFunc = void(&)(int,int);? czy to znaczy, że MyFuncjest odwołaniem do funkcji? co jeśli pominiesz & ?
Rich
7
Tak, jest to odwołanie do funkcji. Jest to równoważne z typedef void (&MyFunc)(int,int);. Jeśli pominiesz, jest &to odpowiedniktypedef void MyFunc(int,int);
R. Martinho Fernandes

Odpowiedzi:

584

Są one równoważne ze standardowego (moje wyróżnienie) (7.1.3.2):

Nazwę typedef można również wprowadzić w deklaracji aliasu. Identyfikator następujący po słowie kluczowym using staje się nazwą typedef, a opcjonalny seq atrybut-specyfikator następujący po identyfikatorze odnosi się do nazwy typedef. Ma taką samą semantykę, jak gdyby został wprowadzony przez specyfikator typedef. W szczególności nie definiuje nowego typu i nie pojawia się w identyfikatorze typu.

Jesse Good
źródło
24
Z odpowiedzi usingsłowo kluczowe wydaje się nadzbiorem typedef. Czy typdefw przyszłości będą przestarzałe?
iammilind
49
Wycofanie niekoniecznie oznacza zamiar usunięcia - jest to tylko bardzo silna rekomendacja, aby wybrać inny sposób.
Żelazny Zbawiciel
18
Ale zastanawiam się, dlaczego nie pozwolili tylko na szablonowanie typedef. Pamiętam, że gdzieś przeczytałem, że wprowadzili usingskładnię właśnie dlatego, że typedefsemantyka nie działała dobrze z szablonami. Co w jakiś sposób przeczy temu, że usingzdefiniowano, że ma dokładnie taką samą semantykę.
celtschk
12
@celtschk: Powód jest omawiany we wniosku n1489. Alias ​​szablonu nie jest aliasem typu, ale aliasem grupy szablonów. Aby dokonać rozróżnienia między typedefodczuwaną potrzebą nowej składni. Pamiętaj również, że pytanie OP dotyczy różnicy między wersjami nieszablonowymi.
Jesse Good
4
Dlaczego więc wprowadzono tę redundancję? 2 składnie w tym samym celu. I nigdy nie widzę, żeby typdefbył przestarzały.
Benoît,
237

Są w dużej mierze takie same, z wyjątkiem tego, że:

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

Zhongming Qu
źródło
29
Szczególnie lubił prostotę odpowiedzi i wskazywał pochodzenie składu.
g24l
1
@ g24l masz na myśli typedef ... prawdopodobnie
Marc.2377
Jaka jest różnica między C i C ++, typedefjeśli mogę zapytać?
McSinyx,
196

Użyciem składni ma przewagę, gdy używane w szablonach. Jeśli potrzebujesz abstrakcji typu, ale musisz także zachować parametr szablonu, aby można go było określić 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; }
4xy
źródło
35
Wskazałem to jednak już w pytaniu. Moje pytanie dotyczy tego, czy nie używasz szablonu, czy jest jakaś różnica w typedef. Na przykład, gdy używasz „Foo foo {init_value};” zamiast „Foo foo (wartość_inicjalizacyjna)” oba powinny robić to samo, ale nie przestrzegają dokładnie tych samych zasad. Zastanawiałem się więc, czy istnieje podobna ukryta różnica w używaniu / typedef.
Klaim
24

Są zasadniczo takie same, ale usingzapewnia alias templatesco jest całkiem przydatne. Dobry przykład, jaki mogłem znaleźć, to:

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

Możemy więc użyć std::add_const_t<T>zamiasttypename std::add_const<T>::type

Validus Oculus
źródło
O ile mi wiadomo, niezdefiniowanym zachowaniem jest dodawanie czegokolwiek w przestrzeni nazw std
jakośpcpc
5
@someonewithpc Nic nie dodawałem, już istnieje, pokazałem tylko przykład użycia nazwy typu. Proszę sprawdzić en.cppreference.com/w/cpp/types/add_cv
Validus Oculus
9

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 propozycji, która moim zdaniem wnosi tutaj dodatkową wartość do dyskusji, szczególnie obawy w komentarzach dotyczących tego, czy typedefsłowo kluczowe jest zostanie w przyszłości oznaczony jako przestarzały lub usunięty za zbędny / stary:

Sugeruje się (ponowne) użycie słowa kluczowego typedef ... w celu wprowadzenia aliasów szablonów:

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

Ta notacja ma tę zaletę, że używa słowa kluczowego już znanego do wprowadzenia aliasu typu. Jednak wyświetla również kilka wad [sic], wśród których zamieszanie związane ze stosowaniem słowa kluczowego znanego z wprowadzenia aliasu dla nazwy typu w kontekście, w którym alias nie oznacza typu, ale szablon; Vecto nie aliasem dla typu i nie powinny być brane za typedef-name. Nazwa Vecjest nazwą rodziny std::vector<•, MyAllocator<•> >- w której kula jest symbolem zastępczym 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 synonimstd::vector<T, MyAllocator<T> > . Dzięki takiemu odczytowi nowa składnia aliasingu wydaje się dość logiczna.

Dla mnie oznacza to ciągłe wsparcie dla typedefsłowa kluczowego w C ++, ponieważ wciąż może sprawić, że kod będzie bardziej czytelny i zrozumiały .

Aktualizacja usingsłowa kluczowego była specjalnie dla szablonów i (jak wskazano w przyjętej odpowiedzi), gdy pracujesz z nie-szablonami usingi typedefsą one mechanicznie identyczne, więc wybór należy do programisty na podstawie czytelności i komunikacji intencji .

RoboticForest
źródło
2

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

marski
źródło