成員型別和別名
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>.