C++11之追踪返回类型

简介: C++11之追踪返回类型

追踪返回类型

追踪返回类型的引入

前面我们讲了auto和decltype在泛式编程中的作用,今天我们在学一个能够让auto和decltype在泛式编程中更加强大的----追踪返回类型


在函数模板中,函数的类型取决于参数的类型,那么当参数类型是动态时,返回类型也是动态的。

下面我以俩数相加函数为例:

template<typename T1, typename T2>
decltype(t1 + t2) Sum(T1& t1, T2& t2)
{
  return t1 + t2;
}


输入的类型不确定 返回的类型不确定

就会写出下面这种返回类型。由于C++编译器是从左往右顺序读入的,按照变量需要先声明后使用的规则,所以这种写法注定是不会编译器允许的。

为了满足C/C++编译器的规则,所以我们可以将计算得到的结果从返回值的形式转为出参。

template<typename T1, typename T2>
void Sum(T1& t1, T2& t2, decltype(t1 + t2)& ret)
{
  ret = t1 + t2;
}


这样的写法的确满足了编译器的规则,但是在使用方上并没有提高便捷性,因为这种的函数模板需要在使用时就需要确定出参类型,因为要先定义。

int main()
{
  int i = 9;
  double j = 3.3;
  double s = 0;
  Sum(i, j, s);
  cout << s << endl;
  return 0;
}


使用追踪返回类型的函数

上述的解决方案都并不是那么的完美,为此C++11引入了新语法 追踪返回类型,类似于lanbda的表达式中的返回类型的写法。

追踪返回类型:将函数的返回类型移到函数最后部分,格式:->返回类型 原本返回类型的位置将会由auto去占位。这样完美就可以让编译器来推导Sum函数模板的返回类型了。auto占位符 与

->return_type 就构成了追踪返回类型函数的俩个基本必备要素。


下面就是使用追踪返回类型的方法实现的俩数相加的函数

template<typename T1, typename T2>
auto Sum(T1& t1, T2& t2)->decltype(t1 + t2)
{
  return 0;
}


常规/普通函数中也可以使用追踪返回类型

这种追踪返回类型不仅仅局限于上述的场景,在常规/普通函数中也可以使用。

int TestFunction1(int i)
{
  return i * 2;
}
auto TestFunction2(int i)->int
{
  return i * 2;
}


返回类型如果和函数的作用域相同或者被包含时则返回类型处可以省略作用域

令人糟糕的是,上述的例子在写法上复杂了,而且易读性也降低了。但它也有好处,返回类型如果和函数的作用域相同或者被包含时则返回类型处可以省略作用域。


下面我创建了一个OuterType类,还有一个成员函数,其中这个函数返回类型的作用域也是在OuterType类内,所以返回类型的作用域就可以省略掉。

class OuterType
{
public:
  struct InnerType
  {
    int i;
  };
  InnerType GetInner();
  InnerType it;
};
auto OuterType::GetInner()->/*OuterType::*/InnerType
{
  return it;
}

C++11引追踪返回类型后,对于模板编程中的类型推导也提高了一个台阶,让泛式编程更加强大 直观的看代码,有种python的感觉,模板函数中没有一个具体的类型。而对于模板的设计者提供了极高的便捷性,编写上也极大的简化了代码的体积。


下面实现了俩数相加、俩数相乘的函数,均采用追踪返回类型的方式。那么在调用者使用的过程中是非常的便捷。

#include <iostream>
using namespace std;
template<typename T1, typename T2>
auto Sum(const T1& t1, const T2& t2)->decltype(t1 + t2)
{
  return t1 + t2;
}
template<typename T1, typename T2>
auto Mul(const T1& t1, const T2& t2)->decltype(t1 * t2)
{
  return t1 * t2;
}
int main()
{
  auto a = 3;
  auto b = 4LL;
  auto pi = 3.14;
  auto c = Mul(Sum(a, b), pi);
  cout << c << endl;
  return 0;
}

在处理函数简化问题上,追踪返回类型也是一把好手

下面定义了令人头疼的pf函数,我们可以使用追踪返回类型很容易的将其简化。而且我们还可以通过is_same模板类来测试俩个函数是否相同。

#include <type_traits>
#include <iostream>
using namespace std;
int (*(*pf())())()
{
  return nullptr;
}
/*
 * auto (*)() -> int(*)()  一个返回函数指针的函数 该函数没有参数,返回值类型为int  设这个函数为x
 * auto pf1() ->auto (*)() -> int(*)()  一个返回a函数指针的函数
 */
auto pf1() ->auto(*)()->int(*)()
{
  return nullptr;
}
int main()
{
  cout << boolalpha << is_same<decltype(pf), decltype(pf1)>::value << endl;
  return 0;
}


参数与返回值类型不同时的转发

追踪返回类型的强大远不止于此,在模板函数中加入追踪返回类型可以实现参数与返回值类型不同时的转发。

在下面的代码中,我们实现了intdoubledoubleint的同名函数,还实现了一个Forward转发函数,其返回类型是通过fool(参数)的方式进行确定的。这样就可以根据输入的类型动态的变化返回的类型。

#include <iostream>
using namespace std;
double foo(int a)
{
  return static_cast<double>(a) + 0.1;
}
int foo(double d)
{
  return static_cast<int>(d);
}
template<class T>
auto Forward(T t)->decltype(foo(t))
{
  return foo(t);
}
int main()
{
  cout << Forward(1.2) << endl;
  cout << Forward(2) << endl;
  return 0;
}


还可以广泛应用于函数指针、普通函数、函数应用等等中。没有返回类型的函数也可以被声明为追踪返回类型,只需要将最后的return_type写为void即可。

目录
相关文章
|
6月前
|
存储 程序员 C语言
c++primer plus 6 读书笔记 第四章 复合类型
c++primer plus 6 读书笔记 第四章 复合类型
|
2月前
|
存储 编译器 程序员
C++类型参数化
【10月更文挑战第1天】在 C++ 中,模板是实现类型参数化的主要工具,用于编写能处理多种数据类型的代码。模板分为函数模板和类模板。函数模板以 `template` 关键字定义,允许使用任意类型参数 `T`,并在调用时自动推导具体类型。类模板则定义泛型类,如动态数组,可在实例化时指定具体类型。模板还支持特化,为特定类型提供定制实现。模板在编译时实例化,需放置在头文件中以确保编译器可见。
37 11
|
3月前
|
安全 程序员 C语言
C++(四)类型强转
本文详细介绍了C++中的四种类型强制转换:`static_cast`、`reinterpret_cast`、`const_cast`和`dynamic_cast`。每种转换都有其特定用途和适用场景,如`static_cast`用于相关类型间的显式转换,`reinterpret_cast`用于低层内存布局操作,`const_cast`用于添加或移除`const`限定符,而`dynamic_cast`则用于运行时的类型检查和转换。通过具体示例展示了如何正确使用这四种转换操作符,帮助开发者更好地理解和掌握C++中的类型转换机制。
|
4月前
|
C++
使用 QML 类型系统注册 C++ 类型
使用 QML 类型系统注册 C++ 类型
85 0
|
5月前
|
编译器 C++ 运维
开发与运维函数问题之函数的返回类型如何解决
开发与运维函数问题之函数的返回类型如何解决
39 6
|
5月前
|
安全 编译器 C++
C++一分钟之-模板元编程实例:类型 traits
【7月更文挑战第15天】C++的模板元编程利用编译时计算提升性能,类型traits是其中的关键,用于查询和修改类型信息。文章探讨了如何使用和避免过度复杂化、误用模板特化及依赖特定编译器的问题。示例展示了`is_same`类型trait的实现,用于检查类型相等。通过`add_pointer`和`remove_reference`等traits,可以构建更复杂的类型转换逻辑。类型traits增强了代码效率和安全性,是深入C++编程的必备工具。
93 12
|
4月前
|
存储 C++
【C/C++学习笔记】string 类型的输入操作符和 getline 函数分别如何处理空白字符
【C/C++学习笔记】string 类型的输入操作符和 getline 函数分别如何处理空白字符
51 0
|
5月前
|
C++
C++一分钟之-类型别名与using声明
【7月更文挑战第20天】在C++中,类型别名和`using`声明提升代码清晰度与管理。类型别名简化复杂类型,如`using ComplexType = std::vector&lt;std::shared_ptr&lt;int&gt;&gt;;`,需注意命名清晰与适度使用。`using`声明引入命名空间成员,避免`using namespace std;`全局污染,宜局部与具体引入,如`using math::pi;`。恰当应用增强代码质量,规避常见陷阱。
87 5
|
4月前
|
设计模式 安全 IDE
C++从静态类型到单例模式
C++从静态类型到单例模式
40 0
|
5月前
|
C++ 开发者
C++一分钟之-概念(concepts):C++20的类型约束
【7月更文挑战第4天】C++20引入了Concepts,提升模板编程的类型约束和可读性。概念定义了模板参数需遵循的规则。常见问题包括过度约束、约束不完整和重载决议复杂性。避免问题的关键在于适度约束、全面覆盖约束条件和理解重载决议。示例展示了如何用Concepts限制模板函数接受的类型。概念将增强模板的安全性和灵活性,但需谨慎使用以防止错误。随着C++的发展,Concepts将成为必备工具。
109 2