C++ 类型推导Auto及decltype

文章讲述了C++11中decltype和auto关键字的作用,auto用于自动类型推导简化代码,decltype则更精确地推导表达式的类型,包括函数返回类型、左值和右值的区别以及在模板和函数模板中的应用。
摘要由CSDN通过智能技术生成

目录

auto

decltype


decltype 和 auto 是 C++11 及其后续版本中引入的两个关键字,它们都用于自动类型推导,但在使用和行为上有一些重要的区别。

auto

auto 关键字在 C++ 中用于自动类型推导。编译器会根据初始化表达式自动推断变量的类型。auto 关键字使代码更加简洁,因为你不需要显式地写出变量的类型。

auto x = 10; // x 的类型被推导为 int
auto y = 3.14; // y 的类型被推导为 double
auto z = "hello"; // z 的类型被推导为 const char*

auto 关键字在编译时推导变量的类型,并且在推导过程中会考虑引用和 const 限定符。

int a = 10;
auto& b = a; // b 是 int& 类型,对 b 的修改会影响到 a
const auto c = a; // c 是 const int 类型,不能修改 c 的值

除此之外使用 auto 会删除引用、const 限定符和 volatile 限定符,如下面代码会输出什么呢?是11 11?还是11 12?

#include <iostream>

using namespace std;

int main( )
{
    int count = 10;
    int& countRef = count;
    auto myAuto = countRef;

    countRef = 11;
    cout << count << " ";

    myAuto = 12;
    cout << count << endl;
}

decltype

下面是decltype的规则,这些规则用于确定表达式的类型:

  1. 如果表达式是一个未加括号的标识符或类成员访问

    • decltype(x) 或 decltype(obj.mem) 的类型是 T,其中 T 是 x 或 mem 的类型。
    • 如果 x 是一个函数名,decltype(x) 是函数类型,而不是函数的返回类型。
    • 如果 x 没有定义,编译器会报错。
  2. 如果表达式是一个函数调用或重载运算符

    • decltype(f()) 或 decltype(operator+()) 的类型是函数的返回类型。
    • 忽略重载运算符两边的括号。
  3. 如果表达式是一个左值

    • decltype((x)) 的类型是 T&,其中 T 是 x 的类型,且 x 是一个左值。
    • 这里的括号是重要的,因为不加括号的话,规则4会应用。
  4. 如果表达式是一个右值

    • decltype(x) 的类型是 T,其中 T 是 x 的类型,且 x 是一个右值。
    • 注意,这与左值引用不同,右值不会获得引用类型。
  5. 引用折叠

    • 如果 decltype 推导出的类型是引用类型(例如 T& 或 T&&),并且这个引用类型被用作模板参数,那么在某些情况下会发生引用折叠(reference collapsing)。
    • 例如,T& &T& &&T&& & 折叠为 T&,而 T&& && 折叠为 T&&
  6. **对于 decltype(auto)**:

    • 在C++14及以后的版本中,decltype(auto) 允许编译器自动推导变量类型,并保留表达式的值类别。
    • 例如,decltype(auto) x = y; 将根据 y 的类型(及其值类别)来推导 x 的类型。
int var;
const int&& fx();
struct A { double x; };
const A* a = new A();
语句类型说明
decltype(fx());const int&&对 const int 的 rvalue 引用
decltype(var);int变量 var 的类型。
decltype(a->x);double成员访问的类型。
decltype((a->x));const double&内部括号导致语句作为表达式而不是成员访问计算。 由于 a 声明为 const 指针,因此类型是对 const double 的引用。

 作为函数模版应用

// decltype_1.cpp
// compile with: cl /EHsc decltype_1.cpp

#include <iostream>
#include <string>
#include <utility>
#include <iomanip>

using namespace std;

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

class X
{
   friend X operator+(const X& x1, const X& x2)
   {
      return X(x1.m_data + x2.m_data);
   }

public:
   X(int data) : m_data(data) {}
   int Dump() const { return m_data;}
private:
   int m_data;
};

int main()
{
   // Integer
   int i = 4;
   cout <<
      "Plus(i, 9) = " <<
      Plus(i, 9) << endl;

   // Floating point
   float dx = 4.0;
   float dy = 9.5;
   cout <<
      setprecision(3) <<
      "Plus(dx, dy) = " <<
      Plus(dx, dy) << endl;

   // String
   string hello = "Hello, ";
   string world = "world!";
   cout << Plus(hello, world) << endl;

   // Custom type
   X x1(20);
   X x2(22);
   X x3 = Plus(x1, x2);
   cout <<
      "x3.Dump() = " <<
      x3.Dump() << endl;
}

当模板被声明而不是被实例化时,编译器会分析 decltype 自变量。 因此,如果在 decltype 自变量中找到非依赖专用化,则它不会被推迟到实例化时间;而是被立即处理,并且在当时诊断产生的所有错误。

以下示例显示了在声明时引发的这类编译器错误:

#include <utility>
template <class T, class ReturnT, class... ArgsT> class IsCallable
{
public:
   struct BadType {};
   template <class U>
   static decltype(std::declval<T>()(std::declval<ArgsT>()...)) Test(int); //C2064. Should be declval<U>
   template <class U>
   static BadType Test(...);
   static constexpr bool value = std::is_convertible<decltype(Test<T>(0)), ReturnT>::value;
};

constexpr bool test1 = IsCallable<int(), int>::value;
static_assert(test1, "PASS1");
constexpr bool test2 = !IsCallable<int*, int>::value;
static_assert(test2, "PASS2");

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

路奇怪

有钱出钱,没钱多出编程主意啊

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值