函数返回类型推导 (Return Type Deduction for Normal Functions)是C++14中新增的特性。
C++11允许lambda函数根据return语句的表达式类型推断返回类型。C++14为一般的函数也提供了这个能力。C++14还拓展了原有的规则,使得函数体并不是{ return expression; }形式的函数也可以使用返回类型推导。
为了启用返回类型推导,函数声明必须将auto 作为返回类型,但没有C++11的后置返回类型说明符:
1 auto DeduceReturnType () ;
如果函数实现中含有多个return语句,这些表达式必须可以推断为相同的类型。
使用返回类型推导的函数可以前向声明,但在定义之前不可以使用。它们的定义在使用它们的翻译单元(translation unit)之中必须是可用的。
这样的函数中可以存在递归,但递归调用必须在函数定义中的至少一个return语句之后:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 auto Correct (int i) { if (i == 1 ) { return i; } else { return Correct (i-1 )+i; } } auto Wrong (int i) { if (i != 1 ) { return Wrong (i-1 )+i; } else { return i; } }
上面描述了这种特性的语法规则,那么适合用在什么地方呢?
最近向Metazion 库里增加HashMap时,便遇到了这种需求。
Metazion里的HashMap默认为1024个桶,每个桶的数据结构采用Map实现。下面是未使用该特性时的代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 template <typename KeyType, typename ValueType , typename CompareType = LessCompare<KeyType> , typename HasherType = IntegerHasher<KeyType> , int BUCKETSIZE = 1024 , typename AllocatorFamily = HeapAllocator<> > class HashMap { DISALLOW_COPY_AND_ASSIGN (HashMap) using Key_t = KeyType; using Value_t = ValueType; using Compare_t = CompareType; using Hasher_t = HasherType; using Allocator_t = AllocatorFamily; using Bucket_t = Map<Key_t, Value_t, Compare_t, Allocator_t>; using BucketEntry_t = Pair<Key_t, Value_t>; using BucketIterator_t = typename Bucket_t::Iterator_t; class Iterator { friend class HashMap ; public : Iterator () : m_owner (nullptr ) , m_bucket (0 ) {} Iterator (const Iterator& other) : m_owner (other.m_owner) , m_bucket (other.m_bucket) , m_iter (other.m_iter) {} Iterator (HashMap* owner, int bucket, BucketIterator_t iter) : m_owner (owner) , m_bucket (bucket) , m_iter (iter) {} ~Iterator () {} Iterator& operator =(const Iterator& other) { if (&other != this ) { m_owner = other.m_owner; m_bucket = other.m_bucket; m_iter = other.m_iter; } return *this ; } BucketEntry_t& operator *() { return m_iter.operator *(); } BucketEntry_t* operator ->() { return m_iter.operator ->(); } ...
HashMap的迭代器,重载*和->符号时,希望能直接返回桶类型Map相对应迭代器的返回值。但Map的模板参数是Key_t和Value_t,其迭代器返回值类型Entry_t是Pair<Key_t, Value_t>,属于其内部实现细节,不应该对外暴露。另一种方式只能是在HashMap里定义一种与Entry_t相同的类型BucketEntry_t,这样能工作。但在设计上,BucketEntry_t使得HashMap迭代器对桶类型Map形成了依赖,一边改动,另一边也要相应修改,这种耦合是不必要的。
函数返回类型 推导即适用于这种场景,通过将返回值定义为 auto ,无需再定义适配类型BucketEntry_t,代码变得简洁,也降低了耦合。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 template <typename KeyType, typename ValueType , typename CompareType = LessCompare<KeyType> , typename HasherType = IntegerHasher<KeyType> , int BUCKETSIZE = 1024 , typename AllocatorFamily = HeapAllocator<> > class HashMap { DISALLOW_COPY_AND_ASSIGN (HashMap) using Key_t = KeyType; using Value_t = ValueType; using Compare_t = CompareType; using Hasher_t = HasherType; using Allocator_t = AllocatorFamily; using Bucket_t = Map<Key_t, Value_t, Compare_t, Allocator_t>; using BucketIterator_t = typename Bucket_t::Iterator_t; class Iterator { friend class HashMap ; ... auto & operator *() { return m_iter.operator *(); } auto * operator ->() { return m_iter.operator ->(); } ...
完整代码在这里 。