35

auto x = initializer;

相当于

decltype(initializer) x = initializer;

或者

decltype((initializer)) x = initializer;

或者都不是?

4

5 回答 5

37

decltype还考虑表达式是rvalue还是lvalue

维基百科说

decltype 表示的类型可以与 auto 推导的类型不同。

#include <vector>
int main()
{
    const std::vector<int> v(1);
    auto a = v[0];        // a has type int
    decltype(v[0]) b = 1; // b has type const int&, the return type of
                        // std::vector<int>::operator[](size_type) const
    auto c = 0;           // c has type int
    auto d = c;           // d has type int
    decltype(c) e;        // e has type int, the type of the entity named by c
    decltype((c)) f = c;  // f has type int&, because (c) is an lvalue
    decltype(0) g;        // g has type int, because 0 is an rvalue
}

这几乎解释了重要的区别。注意decltype(c)decltype((c))不一样!

有时auto并以合作的decltype方式一起工作,例如在以下示例中(取自wiki并稍作修改):

int& foo(int& i);
float foo(float& f);

template <class T>
auto f(T& t) −&gt; decltype(foo(t)) 
{
  return foo(t);
}

维基百科进一步解释了以下语义decltype

与 sizeof 运算符类似,decltype 的操作数是不求值的。非正式地,由 decltype(e) 返回的类型推导如下:

  • 如果表达式 e 引用本地或命名空间范围内的变量、静态成员变量或函数参数,则结果是该变量或参数的声明类型
  • 如果 e 是函数调用或重载运算符调用,则 decltype(e) 表示该函数的声明返回类型
  • 否则,如果 e 是左值,则 decltype(e) 是 T&,其中 T 是 e 的类型;如果 e 是右值,则结果为 T

这些语义旨在满足通用库编写者的需求,同时对新手程序员来说也很直观,因为 decltype 的返回类型总是与源代码中声明的对象或函数的类型完全匹配。更正式地说,规则 1 适用于无括号的 id 表达式和类成员访问表达式。对于函数调用,推导的类型是静态选择函数的返回类型,由重载决议的规则确定。例子:

const int&& foo();
int i;
struct A { double x; };
const A* a = new A();
decltype(foo()) x1; // type is const int&&
decltype(i) x2; // type is int
decltype(a->x) x3; // type is double
decltype((a->x)) x4; // type is const double&

后两种 decltype 调用不同的原因是括号中的表达式 (a->x) 既不是 id 表达式也不是成员访问表达式,因此不表示命名对象。因为表达式是左值,其推导的类型是“对表达式类型的引用”,即 const double&。

于 2011-07-29T07:32:40.767 回答
9

这行不通(而且很丑):

decltype([]() { foo(); }) f = []() { foo(); };

然而

auto f = []() { foo(); };

将要。

于 2011-07-29T08:07:52.100 回答
6

这取决于。autodecltype服务于不同的目的,因此它们不会一对一地映射。

的规则auto是最容易解释的,因为它们与模板参数推导相同。我不会在这里扩展它们,但请注意,这auto&也是auto&&一些可能的用途!

decltype但是有几种情况,您在上面已经说明了其中一些(信息和引用来自 n3290,7.1.6.2 简单类型说明符 [dcl.type.simple]),我将它们分为两类:

  • 当使用标准所称的“未加括号的 id 表达式或未加括号的类成员访问”时
  • 其余的部分!

非正式地,我会说decltype可以对names(对于第一种情况)或expressions进行操作。(形式上并根据语法decltype对表达式进行操作,因此将第一种情况视为一种改进,而将第二种情况视为一个包罗万象的情况。)

当使用带有 decltype 的名称时,您将获得该实体的声明类型。例如decltype(an_object.a_member),它出现在类定义中的成员类型。另一方面,如果我们使用decltype( (an_object.a_member) )我们发现自己处于包罗万象的情况下,我们正在检查表达式的类型,因为它会出现在 code 中

因此,如何涵盖您问题的所有情况:

int initializer;
auto x = initializer; // type int
// equivalent since initializer was declared as int
decltype(initializer) y = initializer;


enum E { initializer };
auto x = initializer; // type E
// equivalent because the expression is a prvalue of type E
decltype( (initializer) ) y = initializer;


struct {
    int const& ializer;
} init { 0 };
auto x = init.ializer; // type int
// not equivalent because declared type is int const&
// decltype(init.ializer) y = init.ializer;
// not equivalent because the expression is an lvalue of type int const&
// decltype( (init.ializer) ) y = init.ializer;
于 2011-07-29T07:50:33.500 回答
2

auto

auto很简单:它会给出与按值模板参数推导相同的类型。auto一致地作用于表达式。

template <class T>
void deduce(T x);

int &refint();
std::string str();
std::string const conststr();

auto i1 = 1; // deduce(1) gives T=int so int i1
auto i2 = i1; // deduce(i1) gives T=int so int i2
auto i3 = refint(); // deduce(refint()) gives T=int so int i3
const auto ci1 = i1; // deduce(i1) gives T=int so const int ci1
auto i4 = ci1; // deduce(ci1) gives T=int so int i4

auto s1 = std::string(); // std::string s1
auto s2 = str(); // std::string s2
auto s3 = conststr(); // std::string s3

在 C++ 中,表达式不能有引用类型(没有refint()类型)。intint&

请注意,表达式的左值性不是右侧表达式(在等号右侧,或一般被复制的东西)的问题。右值1被视为左值i1refint()

对于按值参数(即非引用参数),不仅应用了左值到右值的转换,还应用了数组到指针的转换。const 被忽略。

decltype

decltype是一个非常有用的功能,但界面很糟糕:

decltype对根据名称查找和其他表达式定义的某些表达式有不同的作用!这是使人们讨厌 C++ 的特性类型。

decltype某物的名字

decltype(entity)将进行名称查找并给出实体的声明类型。(entity可以是不合格或合格的标识符,也可以是成员访问权限,例如expr.identifier.)

decltype(f(args))将进行名称查找和重载解析,并给出函数的声明返回类型,而不是表达式的类型:

extern decltype(refint()) ri1; // int &ri1

所以现在我可以通过以下方式检查我对语言的理解decltype

template <class T, class U>
struct sametype {};

template <class T>
struct sametype<T,T> {typedef int same;};

sametype<T,U>::same存在当且 TU是完全相同的类型。

sametype<decltype (i1), int>::same check_i1;
sametype<decltype (i2), int>::same check_i2;
sametype<decltype (i3), int>::same check_i3;
sametype<decltype (i4), int>::same check_i4;

sametype<decltype (ci1), const int>::same check_ci1;
sametype<decltype (ir1), int&>::same check_ir1;

sametype<decltype (s1), std::string>::same check_s1;
sametype<decltype (s2), std::string>::same check_s2;
sametype<decltype (s3), std::string>::same check_s3;

编译很好,所以我没有错!

decltype其他表达方式

否则, whereexpr 不是根据名称查找定义的(不是上述情况之一),例如表达式(expr)decltype实现了一个独特的功能(但 C++ 设计者不会在它上面使用另一个关键字。)

decltype(expr)将给出用 lxrvalueness (lvalue/xvalue/prvalue-ness) 修饰的表达式的类型:

  • prvalue(纯右值)类型T给出T
  • 类型的 xvalueT给出T&&
  • 类型的左值T给出T&

这是函数调用规则的倒数:iff是具有返回类型的函数

  • T&, 表达式f()是一个左值
  • T&&, 表达式f()是一个 xvalue
  • 裸类型(纯对象类型,非引用)T,表达式f()是纯右值

也适用于演员表:裸类型(纯对象类型,非引用)T

  • (T&)expr是一个左值
  • (T&&)expr是一个 xvalue
  • (T)expr是prvalue

引用性是将 lxrvalueness 编码为类型。decltype进行这种编码以保存和传输事物的 lxrvalueness。

expr当您想为表达式取别名时,这很有用:作为函数调用的表达式的 lxrvalueness (正常f(args)或使用类似 的运算符语法)与声明为a @ b的 lxrvalueness 相同alias()decltype(expr) alias();

这可用于通用代码中的纯转发:

// decorated type of an expression
#define EXPR_DEC_TYPE(expr) decltype((expr))

int i;
int &ri = i;
int fi();
int &fri();

EXPR_DEC_TYPE(i) alias_i = i; // int &
EXPR_DEC_TYPE(ri) alias_ri = ri; // int &

EXPR_DEC_TYPE(fi()) alias_fi(); // int alias_fi()
EXPR_DEC_TYPE(fri()) alias_fri(); // int &alias_fri()

请注意,设计为EXPR_DEC_TYPE(foo())等于declexpr(foo())(在大多数情况下),但计算方式不同:

  • declexpr(foo(args))进行名称查找foo,进行重载解析,找到声明,返回声明的确切返回类型,故事结束

  • EXPR_DEC_TYPE(foo(args))找到声明的类型然后计算

    • 返回类型T为裸的表达式的类型(无
      引用)

    • 表达式的lxrvalueness LXR根据声明的返回类型的引用性:lvalue for reference, xvalue of r-reference...

    然后它TLXR装饰类型以获得decT类型:

    • decTT如果LXR = prvalue
    • decTT&&如果LXR = xvalue
    • decTT&如果LXR = lvalue

    EXPR_DEC_TYPE返回decT与声明的返回类型相同。

于 2015-08-15T08:56:06.730 回答
0
  1. 如果initializer是一个数组,那么decltype(x)或者decltype((x)) 不简单地在它上面工作。但是auto会被推导出来一个指针。
  2. 如果initializer是一个函数,那么应用decltype(fp)将推导出函数类型,但是,auto将推导出它的返回类型。

因此,通常auto不能将其视为decltype()您要求的任何版本的替代品。

于 2011-07-29T07:36:06.950 回答