新的auto关键字不能取得"引用"的类型啊,为什么?

zeliai 2013-12-24 04:07:08
我有下面一小段代码,我想用auto关键字返回一个引用,然后改变这个引用的值。我发现不成功。

class A{
int m_i;
public:
A():m_i(10){}
int& Get(){return m_i;}
};
int main(int argc, char* argv[])
{
A obj;
auto i=obj.Get();
i=20;
printf("%d\n",obj.Get());
decltype(obj.Get()) ii=obj.Get();
ii=30;
printf("%d\n",obj.Get());
return 0;
}

这段代码的运行结果是10,30。我期待的结果是20,30
我在新版的GCC/VC上面都测试过了,一样的结果。为什么auto得不到引用的类型,而decltype可以呢?
然后还发现,即使把auto那句话改成使用了forward,也还是不能得到引用:

auto i=forward<int&>(obj.Get());

这是为什么呢? 是auto关键字就是这样的特性,还是我的理解有问题?
望高手解释。
...全文
830 7 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
7 条回复
切换为时间正序
请发表友善的回复…
发表回复
zeliai 2013-12-25
  • 打赏
  • 举报
回复
这样解释就清楚了。
FrankHB1989 2013-12-25
  • 打赏
  • 举报
回复
引用 5 楼 u013101239 的回复:
[quote=引用 3 楼 supermegaboy 的回复:] [quote=引用 楼主 u013101239 的回复:] 我有下面一小段代码,我想用auto关键字返回一个引用,然后改变这个引用的值。我发现不成功。

class A{
    int m_i;
public:
    A():m_i(10){}
    int& Get(){return m_i;}
};
int main(int argc, char* argv[])
{
        A obj;
        auto i=obj.Get();
        i=20;
        printf("%d\n",obj.Get());
        decltype(obj.Get()) ii=obj.Get();
        ii=30;
        printf("%d\n",obj.Get());
        return 0;
}
这段代码的运行结果是10,30。我期待的结果是20,30 我在新版的GCC/VC上面都测试过了,一样的结果。为什么auto得不到引用的类型,而decltype可以呢? 然后还发现,即使把auto那句话改成使用了forward,也还是不能得到引用:

auto i=forward<int&>(obj.Get());
这是为什么呢? 是auto关键字就是这样的特性,还是我的理解有问题? 望高手解释。
是你的理解产生了问题,来源于你对declaration没有形成一个正确的观念。对于如下声明: int & a = ...; 你是“很自然”地把上述语句分成如下两部分:int&和a,所以对于auto i=obj.Get();才会产生auto应该是int&的误解。实际上declaration是如下这样划分的(为叙述方便,偶简化了一些无关痛痒的细节): 对于声明T D,D称为declarator,对于一个普通声明来说,D就是identifier,但对于指针、引用等declarator来说,D不是identifier,D是由指针声明符或引用声明符与identifier一起组合而成的,此时identifier称为declarator-id,例如对于T & D1 来说,D1是declarator-id,&是引用声明符,&D1是declarator,T是被引用的类型,D1这个declarator-id的type(不是指像int、float这种类型修饰符)是引用,T & D1分为两部分,分别为T和&D1,而非T&和D1。 接下来auto是如何推导的呢?它首先定出declarator-id的类型,这个过程不是推导的,而是根据声明的抽象表示形式决定的,如果是&D1这种形式,那么这个declarator-id的类型就是引用,如果是*D1这种形式,就是指针,决定了declarator-id的类型后,再根据模板实参推演的规则推导auto的实际类型,所以,对于auto i=obj.Get();来说,是不会把i推导为引用类型的,只有当auto &i=obj.Get();时,才属于引用类型。 标准的条款: 7.1.6.4 auto specifier .......... 6 Once the type of a declarator-id has been determined according to 8.3, the type of the declared variable using the declarator-id is determined from the type of its initializer using the rules for template argument deduction. Let T be the type that has been determined for a variable identifier d. Obtain P from T by replacing the occurrences of auto with either a new invented type template parameter U or, if the initializer is a braced-init-list (8.5.4), with std::initializer_list<U>. The type deduced for the variable d is then the deduced A determined using the rules of template argument deduction from a function call (14.8.2.1), where P is a function template parameter type and the initializer for d is the corresponding argument. If the deduction fails, the declaration is ill-formed.[/quote] 你的回答非常专业!但是仍然有个问题,如果我把Get函数改成

int* GetP(){return &m_i;}
然后main函数里面如果

auto p=obj.GetP();
那么这个auto就能自动推导出p是一个指针了。你不是说"D是由指针声明符或引用声明符与identifier一起组合而成的"吗? 看起来指针的处理和引用的处理还不太一样啊。 还请继续解释,谢谢![/quote] 所以说,看模板推导规则了。 WG21/N3797 7.1.6.4 4 The type of a variable declared using auto or decltype(auto) is deduced from its initializer. This use is allowed when declaring variables in a block (6.3), in namespace scope (3.3.6), and in a for-init-statement (6.5.3). auto or decltype(auto) shall appear as one of the decl-specifier s in the decl-specifier-seq and the declspecifier-seq shall be followed by one or more init-declarator s, each of which shall have a non-empty initializer . In an initializer of the form ( expression-list ) the expression-list shall be a single assignment-expression . [ Example: auto x = 5; // OK: x has type int const auto *v = &x, u = 6; // OK: v has type const int*, u has type const int static auto y = 0.0; // OK: y has type double auto int r; // error: auto is not a storage-class-specifier auto f() -> int; // OK: f returns int auto g() { return 0.0; } // OK: g returns double auto h(); // OK: h’s return type will be deduced when it is defined —end example ] 7 When a variable declared using a placeholder type is initialized, or a return statement occurs in a function declared with a return type that contains a placeholder type, the deduced return type or variable type is determined from the type of its initializer. In the case of a return with no operand, the initializer is considered to be void(). Let T be the declared type of the variable or return type of the function. If the placeholder is the auto type-specifier , the deduced type is determined using the rules for template argument deduction. If the deduction is for a return statement and the initializer is a braced-init-list (8.5.4), the program is ill-formed. Otherwise, obtain P from T by replacing the occurrences of auto with either a new invented type template parameter U or, if the initializer is a braced-init-list , with std::initializer_-list<U>. Deduce a value for U using the rules of template argument deduction from a function call (14.8.2.1), where P is a function template parameter type and the initializer is the corresponding argument. If the deduction fails, the declaration is ill-formed. Otherwise, the type deduced for the variable or return type is obtained by substituting the deduced U into P. [ Example: auto x1 = { 1, 2 }; // decltype(x1) is std::initializer_list<int> auto x2 = { 1, 2.0 }; // error: cannot deduce element type —end example ] [ Example: const auto &i = expr; The type of i is the deduced type of the parameter u in the call f(expr) of the following invented function template: template <class U> void f(const U& u); —end example ] If the placeholder is the decltype(auto) type-specifier , the declared type of the variable or return type of the function shall be the placeholder alone. The type deduced for the variable or return type is determined as described in 7.1.6.2, as though the initializer had been the operand of the decltype. [ Example: int i; int&& f(); auto x3a = i; // decltype(x3a) is int decltype(auto) x3d = i; // decltype(x3d) is int auto x4a = (i); // decltype(x4a) is int decltype(auto) x4d = (i); // decltype(x4d) is int& auto x5a = f(); // decltype(x5a) is int decltype(auto) x5d = f(); // decltype(x5d) is int&& auto x6a = { 1, 2 }; // decltype(x6a) is std::initializer_list<int> decltype(auto) x6d = { 1, 2 }; // error, { 1, 2 } is not an expression auto *x7a = &i; // decltype(x7a) is int* decltype(auto)*x7d = &i; // error, declared type is not plain decltype(auto) —end example ] 14.8.2.1 Deducing template arguments from a function call [temp.deduct.call] 1 Template argument deduction is done by comparing each function template parameter type (call it P) with the type of the corresponding argument of the call (call it A) as described below. If removing references and cv-qualifiers from P gives std::initializer_list<P 0 > for some P 0 and the argument is an initializer list (8.5.4), then deduction is performed instead for each element of the initializer list, taking P 0 as a function template parameter type and the initializer element as its argument. Otherwise, an initializer list argument causes the parameter to be considered a non-deduced context (14.8.2.5). [ Example: template<class T> void f(std::initializer_list<T>); f({1,2,3}); // T deduced to int f({1,"asdf"}); // error: T deduced to both int and const char* template<class T> void g(T); g({1,2,3}); // error: no argument deduced for T —end example ] For a function parameter pack that occurs at the end of the parameter-declaration-list , the type A of each remaining argument of the call is compared with the type P of the declarator-id of the function parameter pack. Each comparison deduces template arguments for subsequent positions in the template parameter packs expanded by the function parameter pack. When a function parameter pack appears in a non-deduced context (14.8.2.5), the type of that parameter pack is never deduced. [ Example: template<class ... Types> void f(Types& ...); template<class T1, class ... Types> void g(T1, Types ...); template<class T1, class ... Types> void g1(Types ..., T1); void h(int x, float& y) { const int z = x; f(x, y, z); // Types is deduced to int, float, const int g(x, y, z); // T1 is deduced to int; Types is deduced to float, int g1(x, y, z); // error: Types is not deduced g1<int, int, int>(x, y, z); // OK, no deduction occurs } —end example ] 2 If P is not a reference type: — If A is an array type, the pointer type produced by the array-to-pointer standard conversion (4.2) is used in place of A for type deduction; otherwise, — If A is a function type, the pointer type produced by the function-to-pointer standard conversion (4.3) is used in place of A for type deduction; otherwise, — If A is a cv-qualified type, the top level cv-qualifiers of A’s type are ignored for type deduction. 以下超过字符限制略。
zeliai 2013-12-25
  • 打赏
  • 举报
回复
引用 3 楼 supermegaboy 的回复:
[quote=引用 楼主 u013101239 的回复:] 我有下面一小段代码,我想用auto关键字返回一个引用,然后改变这个引用的值。我发现不成功。

class A{
    int m_i;
public:
    A():m_i(10){}
    int& Get(){return m_i;}
};
int main(int argc, char* argv[])
{
        A obj;
        auto i=obj.Get();
        i=20;
        printf("%d\n",obj.Get());
        decltype(obj.Get()) ii=obj.Get();
        ii=30;
        printf("%d\n",obj.Get());
        return 0;
}
这段代码的运行结果是10,30。我期待的结果是20,30 我在新版的GCC/VC上面都测试过了,一样的结果。为什么auto得不到引用的类型,而decltype可以呢? 然后还发现,即使把auto那句话改成使用了forward,也还是不能得到引用:

auto i=forward<int&>(obj.Get());
这是为什么呢? 是auto关键字就是这样的特性,还是我的理解有问题? 望高手解释。
是你的理解产生了问题,来源于你对declaration没有形成一个正确的观念。对于如下声明: int & a = ...; 你是“很自然”地把上述语句分成如下两部分:int&和a,所以对于auto i=obj.Get();才会产生auto应该是int&的误解。实际上declaration是如下这样划分的(为叙述方便,偶简化了一些无关痛痒的细节): 对于声明T D,D称为declarator,对于一个普通声明来说,D就是identifier,但对于指针、引用等declarator来说,D不是identifier,D是由指针声明符或引用声明符与identifier一起组合而成的,此时identifier称为declarator-id,例如对于T & D1 来说,D1是declarator-id,&是引用声明符,&D1是declarator,T是被引用的类型,D1这个declarator-id的type(不是指像int、float这种类型修饰符)是引用,T & D1分为两部分,分别为T和&D1,而非T&和D1。 接下来auto是如何推导的呢?它首先定出declarator-id的类型,这个过程不是推导的,而是根据声明的抽象表示形式决定的,如果是&D1这种形式,那么这个declarator-id的类型就是引用,如果是*D1这种形式,就是指针,决定了declarator-id的类型后,再根据模板实参推演的规则推导auto的实际类型,所以,对于auto i=obj.Get();来说,是不会把i推导为引用类型的,只有当auto &i=obj.Get();时,才属于引用类型。 标准的条款: 7.1.6.4 auto specifier .......... 6 Once the type of a declarator-id has been determined according to 8.3, the type of the declared variable using the declarator-id is determined from the type of its initializer using the rules for template argument deduction. Let T be the type that has been determined for a variable identifier d. Obtain P from T by replacing the occurrences of auto with either a new invented type template parameter U or, if the initializer is a braced-init-list (8.5.4), with std::initializer_list<U>. The type deduced for the variable d is then the deduced A determined using the rules of template argument deduction from a function call (14.8.2.1), where P is a function template parameter type and the initializer for d is the corresponding argument. If the deduction fails, the declaration is ill-formed.[/quote] 你的回答非常专业!但是仍然有个问题,如果我把Get函数改成

int* GetP(){return &m_i;}
然后main函数里面如果

auto p=obj.GetP();
那么这个auto就能自动推导出p是一个指针了。你不是说"D是由指针声明符或引用声明符与identifier一起组合而成的"吗? 看起来指针的处理和引用的处理还不太一样啊。 还请继续解释,谢谢!
ri_aje 2013-12-25
  • 打赏
  • 举报
回复
要引用的话,目前只能用 decltype 了。
飞天御剑流 2013-12-24
  • 打赏
  • 举报
回复
引用 楼主 u013101239 的回复:
我有下面一小段代码,我想用auto关键字返回一个引用,然后改变这个引用的值。我发现不成功。

class A{
    int m_i;
public:
    A():m_i(10){}
    int& Get(){return m_i;}
};
int main(int argc, char* argv[])
{
        A obj;
        auto i=obj.Get();
        i=20;
        printf("%d\n",obj.Get());
        decltype(obj.Get()) ii=obj.Get();
        ii=30;
        printf("%d\n",obj.Get());
        return 0;
}
这段代码的运行结果是10,30。我期待的结果是20,30 我在新版的GCC/VC上面都测试过了,一样的结果。为什么auto得不到引用的类型,而decltype可以呢? 然后还发现,即使把auto那句话改成使用了forward,也还是不能得到引用:

auto i=forward<int&>(obj.Get());
这是为什么呢? 是auto关键字就是这样的特性,还是我的理解有问题? 望高手解释。
是你的理解产生了问题,来源于你对declaration没有形成一个正确的观念。对于如下声明: int & a = ...; 你是“很自然”地把上述语句分成如下两部分:int&和a,所以对于auto i=obj.Get();才会产生auto应该是int&的误解。实际上declaration是如下这样划分的(为叙述方便,偶简化了一些无关痛痒的细节): 对于声明T D,D称为declarator,对于一个普通声明来说,D就是identifier,但对于指针、引用等declarator来说,D不是identifier,D是由指针声明符或引用声明符与identifier一起组合而成的,此时identifier称为declarator-id,例如对于T & D1 来说,D1是declarator-id,&是引用声明符,&D1是declarator,T是被引用的类型,D1这个declarator-id的type(不是指像int、float这种类型修饰符)是引用,T & D1分为两部分,分别为T和&D1,而非T&和D1。 接下来auto是如何推导的呢?它首先定出declarator-id的类型,这个过程不是推导的,而是根据声明的抽象表示形式决定的,如果是&D1这种形式,那么这个declarator-id的类型就是引用,如果是*D1这种形式,就是指针,决定了declarator-id的类型后,再根据模板实参推演的规则推导auto的实际类型,所以,对于auto i=obj.Get();来说,是不会把i推导为引用类型的,只有当auto &i=obj.Get();时,才属于引用类型。 标准的条款: 7.1.6.4 auto specifier .......... 6 Once the type of a declarator-id has been determined according to 8.3, the type of the declared variable using the declarator-id is determined from the type of its initializer using the rules for template argument deduction. Let T be the type that has been determined for a variable identifier d. Obtain P from T by replacing the occurrences of auto with either a new invented type template parameter U or, if the initializer is a braced-init-list (8.5.4), with std::initializer_list<U>. The type deduced for the variable d is then the deduced A determined using the rules of template argument deduction from a function call (14.8.2.1), where P is a function template parameter type and the initializer for d is the corresponding argument. If the deduction fails, the declaration is ill-formed.
FrankHB1989 2013-12-24
  • 打赏
  • 举报
回复
auto推导规则参照模板类型。 decltype规则是单独的,有点麻烦。 decltype(auto)嘛……
FrankHB1989 2013-12-24
  • 打赏
  • 举报
回复
auto& auto&&

65,199

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧