目录
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
的规则,这些规则用于确定表达式的类型:
如果表达式是一个未加括号的标识符或类成员访问:
decltype(x)
或decltype(obj.mem)
的类型是T
,其中T
是x
或mem
的类型。- 如果
x
是一个函数名,decltype(x)
是函数类型,而不是函数的返回类型。- 如果
x
没有定义,编译器会报错。如果表达式是一个函数调用或重载运算符:
decltype(f())
或decltype(operator+())
的类型是函数的返回类型。- 忽略重载运算符两边的括号。
如果表达式是一个左值:
decltype((x))
的类型是T&
,其中T
是x
的类型,且x
是一个左值。- 这里的括号是重要的,因为不加括号的话,规则4会应用。
如果表达式是一个右值:
decltype(x)
的类型是T
,其中T
是x
的类型,且x
是一个右值。- 注意,这与左值引用不同,右值不会获得引用类型。
引用折叠:
- 如果
decltype
推导出的类型是引用类型(例如T&
或T&&
),并且这个引用类型被用作模板参数,那么在某些情况下会发生引用折叠(reference collapsing)。- 例如,
T& &
、T& &&
、T&& &
折叠为T&
,而T&& &&
折叠为T&&
。**对于
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()
{// Integerint i = 4;cout <<"Plus(i, 9) = " <<Plus(i, 9) << endl;// Floating pointfloat dx = 4.0;float dy = 9.5;cout <<setprecision(3) <<"Plus(dx, dy) = " <<Plus(dx, dy) << endl;// Stringstring hello = "Hello, ";string world = "world!";cout << Plus(hello, world) << endl;// Custom typeX 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");