C++/C++11中用于定义类型别名的两种方法:typedef和using
类型别名(type alias)是一个名字,它是某种类型的同义词。使用类型别名有很多好处,它让复杂的类型名字变得简单明了、易于理解和使用,还有助于程序员清楚地知道使用该类型的真实目的。在C++中,任何有效类型都可以有别名。
有两种方法可用于定义类型别名:传统的方法是使用关键字typedef;C++11中规定了一种新的方法,使用别名声明(alias declaration)来定义类型的别名,即使用using.
关键字typedef作为声明语句中的基本数据类型的一部分出现。含有typedef的声明语句定义的不再是变量而是类型别名。和以前的声明语句一样,这里的声明符也可以包含类型修饰,从而也能由基本数据类型构造出复合类型来。
C++11中用关键字using作为别名声明的开始,其后紧跟别名和等号,其作用是把等号左侧的名字规定成等号右侧类型的别名。
类型别名和类型的名字等价,只要是类型的名字能出现的地方,就能使用类型别名。
如果某个类型别名指代的是复合类型或常量,那么把它用到声明语句里就会产生意想不到的后果。
使用typedef定义的别名和使用using定义的别名在语义上是等效的。 唯一的区别是typedef在模板中有一定的局限性,而using没有。
尽管typedef具有更长的历史记录并且在现有代码中可能更常见,但using更通用。
无论是typedef还是using,它们都不会创建新的数据类型,它们仅仅创建现有类型的同义词(synonyms)。不能用于更改现有类型名称的含义。
typedef和using标识符可以声明数组和函数类型,指针和引用,类类型等等。但不能与任何其它标识符组合使用。仅在它们可见的范围内有效:不同的函数或类声明可以定义具有不同含义的相同名字的类型。
typedef的用法包括:定义一种类型的别名;用于struct声明;用来定义与平台无关的类型;用于回调函数;为复杂的声明定义一个新的简单的别名。
typedef是定义了一种类型的新别名,不同于宏,并不是简单的字符串替换。当const和typedef一起出现时,typedef不是简单的字符串替换:
注意:typedef int* INT; const INT p;相当于int* const p;而不是const int* p;
在使用typedef时,不能在声明中有多个存储类关键字:ypedef就像auto,extern, mutable, static, register一样,是一个存储类关键字。即typedef中不能出现这些关键字。
下面是从其他文章中copy的测试代码,详细内容介绍可以参考对应的reference:
typedef.cpp内容如下:
-
#include "typedef.hpp"
-
#include <iostream>
-
#include <vector>
-
#include <string>
-
#include <ios>
-
#include <type_traits>
-
#include <typeinfo>
-
namespace typedef_ {
-
///
-
// reference: https://en.cppreference.com/w/cpp/language/typedef
-
// std::add_const, like many other metafunctions, use member typedefs
-
template< class T>
-
struct add_const {
-
typedef const T type;
-
};
-
int test_typedef_1()
-
{
-
// simple typedef
-
typedef unsigned long ulong;
-
// the following two objects have the same type
-
unsigned long l1;
-
ulong l2;
-
// more complicated typedef
-
typedef int int_t, *intp_t, (&fp)(int, ulong), arr_t[10];
-
// the following two objects have the same type
-
int a1[10];
-
arr_t a2;
-
for (int i = 0; i < 10; ++i)
-
a2[i] = i;
-
for (int i = 0; i < 10; ++i)
-
fprintf(stdout, "a2 value: %d\n", a2[i]);
-
// common C idiom to avoid having to write "struct S"
-
typedef struct {int a; int b;} S, *pS;
-
// the following two objects have the same type
-
pS ps1;
-
S* ps2;
-
// error: storage-class-specifier cannot appear in a typedef declaration
-
// typedef static unsigned int uint;
-
// typedef can be used anywhere in the decl-specifier-seq
-
long unsigned typedef int long ullong;
-
// more conventionally spelled "typedef unsigned long long int ullong;"
-
// std::add_const, like many other metafunctions, use member typedefs
-
/*template< class T>
-
struct add_const { // error: a template declaration cannot appear at block scope
-
typedef const T type;
-
};*/
-
typedef struct Node {
-
//struct listNode* next; // declares a new (incomplete) struct type named listNode
-
} listNode; // error: conflicts with the previously declared struct name
-
struct Node2 {
-
int data;
-
struct Node2* nextptr;
-
};
-
// 使用typedef可以将上面的Node2改写为:
-
typedef struct Node3 Node3;
-
struct Node3 {
-
int data;
-
Node3* nextptr;
-
};
-
typedef double wages; // wages是double的同义词
-
typedef wages base, *p; // base是double的同义词,p是double*的同义词
-
return 0;
-
}
-
-
// reference: http://www.cplusplus.com/doc/tutorial/other_data_types/
-
int test_typedef_2()
-
{
-
// In C++, there are two syntaxes for creating such type aliases:
-
// The first, inherited from the C language, uses the typedef keyword:
-
// typedef existing_type new_type_name;
-
{
-
typedef char C;
-
typedef unsigned int WORD;
-
typedef char * pChar;
-
typedef char field [50];
-
C mychar, anotherchar, *ptc1;
-
WORD myword;
-
pChar ptc2;
-
field name;
-
}
-
// a second syntax to define type aliases was introduced in the C++ language:
-
// using new_type_name = existing_type;
-
{
-
//the same type aliases as above could be defined as:
-
using C = char;
-
using WORD = unsigned int;
-
using pChar = char *;
-
using field = char [50];
-
}
-
{
-
// 注意: typedef int* INT; const INT p;相当于int* const p;而不是const int* p;
-
typedef int* INT;
-
int a[] = {1, 2, 3};
-
const INT p1 = &a[0];
-
const int* p2 = &a[0];
-
//++p1; // error: increment of read-only variable 'p1'
-
p1[0] = -100;
-
fprintf(stdout, "a[0]: %d\n", a[0]);
-
++p2;
-
//p2[0] = -200; // error: assignment of read-only location '*p2'
-
}
-
return 0;
-
}
-
-
// reference: https://msdn.microsoft.com/en-us/library/dn467695.aspx
-
void actual_function(int arg) { fprintf(stdout, "value: %d\n", arg); }
-
template<typename T>
-
using ptr2 = T*;
-
template<typename T>
-
struct MyAlloc {
-
typedef T value_type;
-
MyAlloc() { }
-
template<typename U>
-
MyAlloc(const MyAlloc<U>&) { }
-
bool operator == (const MyAlloc&) const { return true; }
-
bool operator != (const MyAlloc&) const { return false; }
-
T* allocate(const size_t n) const {
-
fprintf(stdout, "start allocate\n");
-
if (n == 0) {
-
return nullptr;
-
}
-
if (n > static_cast<size_t>(-1) / sizeof(T)) {
-
throw std::bad_array_new_length();
-
}
-
void* const pv = malloc(n * sizeof(T));
-
if (!pv) {
-
throw std::bad_alloc();
-
}
-
return static_cast<T*>(pv);
-
}
-
void deallocate(T* const p, size_t) const {
-
fprintf(stdout, "start deallocate\n");
-
free(p);
-
}
-
};
-
using MyIntVector = std::vector<int, MyAlloc<int>>;
-
int test_typedef_3()
-
{
-
{ // An alias does not introduce a new type and cannot change the meaning of an existing type name
-
// C++11
-
using counter = long;
-
// C++03 equivalent:
-
// typedef long counter;
-
}
-
{ // Aliases also work with function pointers
-
// C++11
-
using func = void(*)(int);
-
// C++03 equivalent:
-
// typedef void (*func)(int);
-
// func can be assigned to a function pointer value
-
func fptr = &actual_function;
-
fptr(10);
-
}
-
{ // A limitation of the typedef mechanism is that it doesn't work with templates. However, the type alias syntax in C++11 enables the creation of alias templates:
-
//template<typename T> using ptr = T*; // error: a template declaration cannot appear at block scope
-
// the name 'ptr<T>' is now an alias for pointer to T
-
ptr2<int> ptr_int;
-
}
-
{ // an alias template with a custom allocator
-
MyIntVector foov = { 1701, 1764, 1664 };
-
for (auto a: foov)
-
fprintf(stdout, " %d ", a);
-
fprintf(stdout, "\n");
-
}
-
return 0;
-
}
-
///
-
// reference: https://zh.wikipedia.org/zh-hk/Typedef
-
int do_math(float arg1, int arg2) { return arg2; }
-
int call_a_func(int (*call_this)(float, int))
-
{
-
int output = call_this(5.5, 7);
-
return output;
-
}
-
// 将上面改为使用typedef,回调函数
-
typedef int (*MathFunc)(float, int);
-
int call_a_func2(MathFunc call_this)
-
{
-
int output = call_this(5.5, 7);
-
return output;
-
}
-
int test_typedef_4()
-
{
-
int final_result = call_a_func(&do_math);
-
fprintf(stdout, "final_result: %d\n", final_result);
-
int final_result2 = call_a_func2(&do_math);
-
fprintf(stdout, "final_result2: %d\n", final_result2);
-
return 0;
-
}
-
-
// reference: https://en.cppreference.com/w/cpp/language/type_alias
-
// type alias, identical to
-
// typedef std::ios_base::fmtflags flags;
-
using flags = std::ios_base::fmtflags;
-
// the name 'flags' now denotes a type:
-
flags fl = std::ios_base::dec;
-
// type alias, identical to
-
// typedef void (*func)(int, int);
-
using func = void (*) (int, int);
-
// the name 'func' now denotes a pointer to function:
-
void example(int, int) {}
-
func f = example;
-
// alias template
-
template<class T>
-
using ptr = T*;
-
// the name 'ptr<T>' is now an alias for pointer to T
-
ptr<int> x;
-
// type alias used to hide a template parameter
-
template<class CharT>
-
using mystring = std::basic_string<CharT, std::char_traits<CharT>>;
-
mystring<char> str;
-
// type alias can introduce a member typedef name
-
template<typename T>
-
struct Container { using value_type = T; };
-
// which can be used in generic programming
-
template<typename Container>
-
void g(const Container& c)
-
{
-
typename Container::value_type n;
-
fprintf(stdout, "type: %s\n", typeid(n).name());
-
}
-
// type alias used to simplify the syntax of std::enable_if
-
template<typename T>
-
using Invoke = typename T::type;
-
template<typename Condition>
-
using EnableIf = Invoke<std::enable_if<Condition::value>>;
-
template<typename T, typename = EnableIf<std::is_polymorphic<T>>>
-
int fpoly_only(T t) { return 1; }
-
struct S { virtual ~S() {} };
-
int test_typedef_5()
-
{
-
Container<int> c;
-
g(c); // Container::value_type will be int in this function
-
//fpoly_only(c); // error: enable_if prohibits this
-
S s;
-
fpoly_only(s); // okay: enable_if allows this
-
return 0;
-
}
-
} // namespace typedef_
CMakeLists.txt内容如下:
-
PROJECT(CppBaseTest)
-
CMAKE_MINIMUM_REQUIRED(VERSION 3.0)
-
# 支持C++11
-
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -Wall -O2 -std=c11")
-
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -Wall -O2 -std=c++11")
-
# 支持C++14, when gcc version > 5.1, use -std=c++14 instead of c++1y
-
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -Wall -O2 -std=c++1y")
-
MESSAGE(STATUS "project source dir: ${PROJECT_SOURCE_DIR}")
-
SET(PATH_SRC_FILES ${PROJECT_SOURCE_DIR}/./../../demo/CppBaseTest)
-
MESSAGE(STATUS "path src files: ${PATH_SRC_FILES}")
-
# 指定头文件的搜索路径
-
INCLUDE_DIRECTORIES(${PATH_SRC_FILES})
-
# 递归查询所有匹配的文件:*.cpp
-
FILE(GLOB_RECURSE CPP_LIST ${PATH_SRC_FILES}/*.cpp)
-
FILE(GLOB_RECURSE C_LIST ${PATH_SRC_FILES}/*.c)
-
#MESSAGE(STATUS "cpp list: ${C_LIST}")
-
# 编译可执行程序
-
ADD_EXECUTABLE(CppBaseTest ${CPP_LIST} ${C_LIST})
-
# 用来为target添加需要链接的共享库,指定工程所用的依赖库,包括动态库和静态库
-
TARGET_LINK_LIBRARIES(CppBaseTest pthread)
build.sh脚本内容如下:
-
#! /bin/bash
-
real_path=$(realpath $0)
-
dir_name=`dirname "${real_path}"`
-
echo "real_path: ${real_path}, dir_name: ${dir_name}"
-
new_dir_name=${dir_name}/build
-
mkdir -p ${new_dir_name}
-
cd ${new_dir_name}
-
cmake ..
-
make
-
cd -
编译及测试方法如下:首先执行build.sh,然后再执行./build/CppBaseTest即可。