auto和decltype之间的关系

时间:2011-07-29 07:25:44

标签: c++ c++11 auto type-inference decltype

auto x = initializer;

相当于

decltype(initializer) x = initializer;

decltype((initializer)) x = initializer;
是不是?

5 个答案:

答案 0 :(得分:37)

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

Wikipedia says

  

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))不一样!

有时autodecltype以合作的方式共同 ,例如以下示例(取自wiki,并稍加修改) :

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

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

维基百科decltype进一步explains the semantics如下:

  

与sizeof运算符类似,decltype的操作数未被评估。非正式地,decltype(e)返回的类型推导如下:

     
      
  • 如果表达式e引用本地或命名空间作用域中的变量,静态成员变量或函数参数,则结果是变量或参数的声明类型
  •   
  • 如果e是函数调用或重载操作符调用,则decltype(e)表示该函数的声明返回类型
  •   
  • 否则,如果e是左值,则decltype(e)是T&amp;,其中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-expression也不是成员访问表达式,因此不表示命名对象。因为表达式是左值,它的推导类型是“对表达式类型的引用”,或者是const double&amp;。

答案 1 :(得分:9)

这不起作用(而且很难看):

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

,而

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

意愿。

答案 2 :(得分:6)

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

auto的规则最容易解释,因为它们与模板参数扣除相同。我不会在此处展开​​它们,但请注意auto&auto&&也是一些可能的用途!

然而,

decltype有几种情况,其中一些已在上面说明(信息和引自n3290,7.1.6.2简单类型说明符[dcl.type.simple]),我将其分为两类: / p>

  • 当使用标准称为“未加密码的id-expression或未加密码的类成员访问”时
  • 其余的!

非正式地,我会说decltype可以对名称(对于第一种情况)或表达式进行操作。 (正式并且根据语法decltype对表达式进行操作,因此将第一种情况视为一种细化,将第二种情况视为一种全能。)

使用带有decltype的名称时,您将获得该实体的声明的类型。因此,例如decltype(an_object.a_member)是类定义中出现的成员类型。另一方面,如果我们使用decltype( (an_object.a_member) ),我们发现自己处于全包状态,我们正在检查表达式的类型,就像在代码中一​​样。

因此,如何涵盖所有问题:

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;

答案 3 :(得分: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()类型int而不是int&)。

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

对于by值参数(即非参考参数),不仅应用了左值转换的左值,而且还应用了数组到指针的转换。 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存在 iff 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;

compiles fine,所以我没错!

decltype其他表达式

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

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

  • T类型的prvalue(纯右值)给出了T
  • T类型的xvalue提供T&&
  • T类型的左值给出了T&

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

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

以及对于强制类型(纯对象类型,非引用)T

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

Reference-ness是将lxrvalueness编码为类型。 decltype执行此编码以保存和传输事物的lxrvalueness。

当您想要为表达式设置别名时,这非常有用:作为函数调用的表达式expr的lxrvalueness(正常f(args)或运算符语法如a @ b)是与声明为alias()

decltype(expr) alias();的lxrvalueness相同

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

// 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 声明的返回类型:参考的左值,r参考的xvalue ......

    然后用 LXR 装饰T类型以获取decT类型:

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

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

答案 4 :(得分:0)

  1. 如果initializer是一个数组,那么decltype(x)decltype((x)) 不要简单地工作。但是auto将被推断为a 指针。
  2. 如果initializer是一个功能,则应用decltype(fp)即可 然而,推导到函数类型,auto将推导出它 返回类型。
  3. 因此,一般来说auto不能被视为您所询问的任何decltype()版本的替代。