decltype 说明符 (C++11 起)

来自cppreference.com
< cpp‎ | language


 
 
C++ 语言
 
 

检查实体的声明类型,或表达式的类型和值类别。

语法

decltype ( 实体 ) (1)
decltype ( 表达式 ) (2)

解释

1) 如果实参是没有括号的标识表达式或没有括号的类成员访问表达式,那么 decltype 产生该表达式指名的实体的类型。如果没有这种实体或该实参指名了一组重载函数,那么程序非良构。

如果实参是指名某个结构化绑定的没有括号的标识表达式,那么 decltype 产生其被引用类型(在关于结构化绑定声明的说明中有所描述)。

(C++17 起)

如果实参是指名某个非类型模板形参的没有括号的标识表达式,那么 decltype 生成该模板形参的类型(当该模板形参以占位符类型声明时,类型会先进行任何所需的类型推导)。即使实体是模板形参对象(为常量对象),其类型仍是非 const 的。

(C++20 起)
2) 如果实参是类型为 T 的任何其他表达式,且
a) 如果 表达式值类别亡值,则 decltype 产生 T&&
b) 如果 表达式 的值类别是左值,则 decltype 产生 T&
c) 如果 表达式 的值类别是纯右值,则 decltype 产生 T

如果 表达式 是返回类类型纯右值的函数调用,或是右操作数为这种函数调用的逗号表达式,那么不会对该纯右值引入临时量。

(C++17 前)

如果 表达式除了(可带括号的)立即调用以外的 (C++20 起)纯右值,那么不会从该纯右值实质化临时对象:即这种纯右值没有结果对象。

(C++17 起)
由于并未创建临时对象,该类型不需要是完整类型或拥有可用的析构函数,而且类型可以是抽象的。此规则不适用于其子表达式:decltype(f(g())) 中,g() 必须有完整类型,但 f() 不必。

注意如果对象的名字带有括号,那么它会被当做普通的左值表达式,因此 decltype(x)decltype((x)) 通常是不同的类型。

在所声明的类型难以或不可能以标准写法声明时,decltype 很有用,例如 lambda 相关类型或依赖于模板形参的类型。

注解

功能特性测试宏 标准 功能特性
__cpp_decltype 200707L (C++11) decltype

关键词

decltype

示例

#include <cassert>
#include <iostream>
#include <type_traits>
 
struct A { double x; };
const A* a;
 
decltype(a->x) y;       // y 的类型是 double(其声明类型)
decltype((a->x)) z = y; // z 的类型是 const double&(左值表达式)
 
template<typename T, typename U>
auto add(T t, U u) -> decltype(t + u) // 返回类型依赖于模板形参
{                                     // C++14 开始可以推导返回类型
    return t + u;
}
 
const int& getRef(const int* p) { return *p; }
static_assert(std::is_same_v<decltype(getRef), const int&(const int*)>);
auto getRefFwdBad(const int* p) { return getRef(p); }
static_assert(std::is_same_v<decltype(getRefFwdBad), int(const int*)>,
    "仅返回 auto 并不能完美转发。");
decltype(auto) getRefFwdGood(const int* p) { return getRef(p); }
static_assert(std::is_same_v<decltype(getRefFwdGood), const int&(const int*)>,
    "返回 decltype(auto) 完美转发返回类型。");
 
// 另一种写法:
auto getRefFwdGood1(const int* p) -> decltype(getRef(p)) { return getRef(p); }
static_assert(std::is_same_v<decltype(getRefFwdGood1), const int&(const int*)>,
    "返回 decltype(返回表达式) 也完美转发返回类型。");
 
int main() 
{
    int i = 33;
    decltype(i) j = i * 2;
    static_assert(std::is_same_v<decltype(i), decltype(j)>);
    assert(i == 33 && 66 == j);
 
    auto f = [i](int av, int bv) -> int { return av * bv + i; };
    auto h = [i](int av, int bv) -> int { return av * bv + i; };
    static_assert(!std::is_same_v<decltype(f), decltype(h)>,
        "lambda 函数的类型是独有且无名的");
 
    decltype(f) g = f;
    std::cout << f(3, 3) << ' ' << g(3, 3) << '\n';
}

输出:

42 42

引用

延伸内容
  • C++23 标准(ISO/IEC 14882:2024):
  • 9.2.9.5 Decltype specifiers [dcl.type.decltype]
  • C++20 标准(ISO/IEC 14882:2020):
  • 9.2.8.4 Decltype specifiers [dcl.type.decltype]
  • C++17 标准(ISO/IEC 14882:2017):
  • TBD Decltype specifiers [dcl.type.decltype]
  • C++14 标准(ISO/IEC 14882:2014):
  • TBD Decltype specifiers [dcl.type.decltype]
  • C++11 标准(ISO/IEC 14882:2011):
  • TBD Decltype specifiers [dcl.type.decltype]

参阅

auto 说明符(C++11) 指定从表达式推导的类型
(C++11)
获取到其实参的引用,用于不求值语境中
(函数模板)
(C++11)
检查两个类型是否相同
(类模板)