一文详解C++11中decltype的使用

寻技术 C/C++编程 2024年05月18日 146

The decltype type specifier yields the type of a specified expression. The decltype type specifier, together with the auto keyword, is useful primarily to developers who write template libraries. Use auto and decltype to declare a template function whose return type depends on the types of its template arguments. Or,use auto and decltype to declare a template function that wraps a call to another function, and then returns the return type of the wrapped function.

The compiler uses the following rules to determine the type of the expression parameter:

(1)、If the expression parameter is an identifier or a class member access, decltype(expression) is the type of the entity named by expression. If there is no such entity or the expression parameter names a set of overloaded functions,the compiler yields an error message.

(2)、If the expression parameter is a call to a function or an overloaded operator function, decltype(expression) is the return type of the function. Parentheses around an overloaded operator are ignored.

(3)、If the expression parameter is an rvalue, decltype(expression) is the type of expression. If the expression parameter is an lvalue, decltype(expression) is an lvalue reference to the type of expression.

decltype is useful when declaring types that are difficult or impossible to declare using standard notation, like lambda-related types or types that depend on template parameters.

decltype is a standard C++11 feature. It is an "operator" which takes an expression and returns a type.

decltype与auto关键字一样,用于进行编译时类型推导,不过它与auto是有一些区别的。decltype的类型推导并不是像auto一样是从变量声明的初始化表达式获得变量的类型,而是总是以一个普通表达式作为参数,返回该表达式的类型,而且decltype并不会对表达式进行求值。

decltype关键字用于查询表达式的类型,并不会对表达式进行求值。decltype的作用是获得一个变量或表达式的类型。decltype 不会执行表达式而auto会,decltype仅仅推论一下表达式的类型。

对于decltype( e )而言,其判别结果受以下条件的影响:

(1)、如果e是一个标识符或者类成员的访问表达式,则decltype(e)就是e所代表的实体的类型。如果没有这种类型或者e是一个重载函数集,那么程序是错误的;

(2)、如果e是一个函数调用或者一个重载操作符调用(忽略e外面的括号),那么decltype(e)就是该函数的返回类型;

(3)、如果e不属于以上所述的情况,则假设e的类型是 T:当e是一个左值时,decltype(e)就是T&;否则(e是一个右值),decltype(e)是T。

auto是为所有人准备的,而decltype是提供给模板开发者的。

在C++中,decltype作为操作符,用于查询表达式的数据类型。decltype在C++11标准制定时引入,主要是为泛型编程而设计,以解决泛型编程中,由于有些类型由模板参数决定,而难以表示的问题。

下面是从其他文章中copy的测试代码,详细内容介绍可以参考对应的reference:

#include "decltype.hpp"
#include <iostream>
#include <string>
#include <utility>
#include <iomanip>
//
// reference: http://en.cppreference.com/w/cpp/language/decltype
struct A { double x; };
const A* a = new A{ 0 };
decltype(a->x) y;       // type of y is double (declared type)
decltype((a->x)) z = y; // type of z is const double& (lvalue expression)
template<typename T, typename U>
auto add(T t, U u) -> decltype(t + u); // return type depends on template parameters
int test_decltype1()
{
	int i = 33;
	decltype(i) j = i * 2;
	std::cout << "i = " << i << ", " << "j = " << j << '\n';
	auto f = [](int a, int b) -> int
	{
		return a * b;
	};
	decltype(f) g = f; // the type of a lambda function is unique and unnamed
	i = f(2, 2);
	j = g(3, 3);
	std::cout << "i = " << i << ", " << "j = " << j << '\n';
	return 0;
}
///
// reference: https://msdn.microsoft.com/zh-cn/library/dd537655.aspx
template<typename T1, typename T2>
auto Plus(T1&& t1, T2&& t2) ->
decltype(std::forward<T1>(t1) +std::forward<T2>(t2))
{
	return std::forward<T1>(t1) +std::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 test_decltype2()
{
	// Integer 
	int i = 4;
	std::cout << "Plus(i, 9) = " << Plus(i, 9) << std::endl;
	// Floating point
	float dx = 4.0;
	float dy = 9.5;
	std::cout << std::setprecision(3) << "Plus(dx, dy) = " << Plus(dx, dy) << std::endl;
	// String
	std::string hello = "Hello, ";
	std::string world = "world!";
	std::cout << Plus(hello, world) << std::endl;
	// Custom type
	X x1(20);
	X x2(22);
	X x3 = Plus(x1, x2);
	std::cout << "x3.Dump() = " << x3.Dump() << std::endl;
	return 0;
}
///
// reference: http://thbecker.net/articles/auto_and_decltype/section_06.html
struct S {
	S(){ m_x = 42; }
	int m_x;
};
int x;
const int cx = 42;
const int& crx = x;
const S* p = new S();
// x is declared as an int: x_type is int.
typedef decltype(x) x_type;
// auto also deduces the type as int: a_ is an int.
auto a_ = x;
// cx is declared as const int: cx_type is const int.
typedef decltype(cx) cx_type;
// auto drops the const qualifier: b is int.
auto b = cx;
// crx is declared as const int&: crx_type is const int&.
typedef decltype(crx) crx_type;
// auto drops the reference and the const qualifier: c is an int.
auto c = crx;
// S::m_x is declared as int: m_x_type is int
// Note that p->m_x cannot be assigned to. It is effectively
// constant because p is a pointer to const. But decltype goes
// by the declared type, which is int.
typedef decltype(p->m_x) m_x_type;
// auto sees that p->m_x is const, but it drops the const
// qualifier. Therefore, d is an int.
auto d = p->m_x;

GitHub: https://github.com/fengbingchun/Messy_Test

关闭

用微信“扫一扫”