成员类型和别名
class
或 struct
也可以定义成员类型别名,它们是类本身中包含的类型别名,并被视为类本身的成员。
struct IHaveATypedef {
typedef int MyTypedef;
};
struct IHaveATemplateTypedef {
template<typename T>
using MyTemplateTypedef = std::vector<T>;
};
与静态成员一样,使用范围运算符::
访问这些 typedef。
IHaveATypedef::MyTypedef i = 5; // i is an int.
IHaveATemplateTypedef::MyTemplateTypedef<int> v; // v is a std::vector<int>.
与普通类型别名一样,每个成员类型别名都允许引用其定义之前但不之后定义或别名的任何类型。同样,类定义之外的 typedef 可以引用类定义中的任何可访问的 typedef,只要它在类定义之后。
template<typename T>
struct Helper {
T get() const { return static_cast<T>(42); }
};
struct IHaveTypedefs {
// typedef MyTypedef NonLinearTypedef; // Error if uncommented.
typedef int MyTypedef;
typedef Helper<MyTypedef> MyTypedefHelper;
};
IHaveTypedefs::MyTypedef i; // x_i is an int.
IHaveTypedefs::MyTypedefHelper hi; // x_hi is a Helper<int>.
typedef IHaveTypedefs::MyTypedef TypedefBeFree;
TypedefBeFree ii; // ii is an int.
成员类型别名可以使用任何访问级别声明,并且将遵循相应的访问修饰符。
class TypedefAccessLevels {
typedef int PrvInt;
protected:
typedef int ProInt;
public:
typedef int PubInt;
};
TypedefAccessLevels::PrvInt prv_i; // Error: TypedefAccessLevels::PrvInt is private.
TypedefAccessLevels::ProInt pro_i; // Error: TypedefAccessLevels::ProInt is protected.
TypedefAccessLevels::PubInt pub_i; // Good.
class Derived : public TypedefAccessLevels {
PrvInt prv_i; // Error: TypedefAccessLevels::PrvInt is private.
ProInt pro_i; // Good.
PubInt pub_i; // Good.
};
这可用于提供抽象级别,允许类设计者在不破坏依赖它的代码的情况下更改其内部工作。
class Something {
friend class SomeComplexType;
short s;
// ...
public:
typedef SomeComplexType MyHelper;
MyHelper get_helper() const { return MyHelper(8, s, 19.5, "shoe", false); }
// ...
};
// ...
Something s;
Something::MyHelper hlp = s.get_helper();
在这种情况下,如果帮助程序类从 SomeComplexType
更改为其他类型,则只需要修改 typedef
和 friend
声明; 只要助手类提供相同的功能,任何使用它作为 Something::MyHelper
而不是按名称指定它的代码通常仍然可以在没有任何修改的情况下工作。通过这种方式,我们最小化了底层实现更改时需要修改的代码量,这样只需要在一个位置更改类型名称。
如果需要的话,这也可以与 decltype
结合使用。
class SomethingElse {
AnotherComplexType<bool, int, SomeThirdClass> helper;
public:
typedef decltype(helper) MyHelper;
private:
InternalVariable<MyHelper> ivh;
// ...
public:
MyHelper& get_helper() const { return helper; }
// ...
};
在这种情况下,由于 decltype
,改变 SomethingElse::helper
的实现将自动改变我们的 typedef。当我们想要改变 helper
时,这可以最大限度地减少必要的修改次数,从而最大限度地减少人为错误的风险。
然而,与所有事情一样,这可能会走得太远。例如,如果 typename 仅在内部使用一次或两次,而在外部使用零次,则无需为其提供别名。如果它在整个项目中使用了数百或数千次,或者如果它有足够长的名称,那么将它作为 typedef 而不是始终以绝对术语使用它会很有用。必须平衡向前兼容性和便利性以及产生的不必要噪声量。
这也可以与模板类一起使用,以提供对类外部模板参数的访问。
template<typename T>
class SomeClass {
// ...
public:
typedef T MyParam;
MyParam getParam() { return static_cast<T>(42); }
};
template<typename T>
typename T::MyParam some_func(T& t) {
return t.getParam();
}
SomeClass<int> si;
int i = some_func(si);
这通常与容器一起使用,容器通常提供其元素类型和其他帮助程序类型,作为成员类型别名。例如,C++标准库中的大多数容器提供以下 12 种帮助程序类型,以及它们可能需要的任何其他特殊类型。
template<typename T>
class SomeContainer {
// ...
public:
// Let's provide the same helper types as most standard containers.
typedef T value_type;
typedef std::allocator<value_type> allocator_type;
typedef value_type& reference;
typedef const value_type& const_reference;
typedef value_type* pointer;
typedef const value_type* const_pointer;
typedef MyIterator<value_type> iterator;
typedef MyConstIterator<value_type> const_iterator;
typedef std::reverse_iterator<iterator> reverse_iterator;
typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
};
在 C++ 11 之前,它还常用于提供各种类型的“模板 typedef
”,因为该功能尚不可用; 这些在引入别名模板时变得不那么常见,但在某些情况下仍然有用(并且在其他情况下与别名模板结合使用,这对于获取复杂类型的单个组件(如函数指针)非常有用)。他们通常使用名称 type
作为其类型别名。
template<typename T>
struct TemplateTypedef {
typedef T type;
}
TemplateTypedef<int>::type i; // i is an int.
这通常与具有多个模板参数的类型一起使用,以提供定义一个或多个参数的别名。
template<typename T, size_t SZ, size_t D>
class Array { /* ... */ };
template<typename T, size_t SZ>
struct OneDArray {
typedef Array<T, SZ, 1> type;
};
template<typename T, size_t SZ>
struct TwoDArray {
typedef Array<T, SZ, 2> type;
};
template<typename T>
struct MonoDisplayLine {
typedef Array<T, 80, 1> type;
};
OneDArray<int, 3>::type arr1i; // arr1i is an Array<int, 3, 1>.
TwoDArray<short, 5>::type arr2s; // arr2s is an Array<short, 5, 2>.
MonoDisplayLine<char>::type arr3c; // arr3c is an Array<char, 80, 1>.