返回指向常量数据成员和 'auto' 关键字的常量指针。有点糊涂
Returning a const pointer to a const data member and the 'auto' keyword. A bit confused
最近在学习C++,今天刚接触到const和const正确性的概念。为了更好地理解这个理论,我一直在编写一系列简单的程序来确保我正确理解了这个概念。我以为我明白了一切,但是当在其中一个程序中使用 auto 关键字时,我似乎有点卡住了。
为了测试我是否理解 const 指针的工作原理,我编写了一个简单的程序。我不会费心发布整件事,因为只有两部分是相关的。我有一个 class 和一个 int 类型的 const 数据成员:
const int tryToChangeMe;
在这个class中我还有一个成员函数return是一个指向上面的const int:
的const指针
const int* const MyClass::test()
{
return &tryToChangeMe;
}
然后在我的主函数中调用上述函数,使用 auto 关键字。为了测试我认为我所知道的 const 是正确的,然后我尝试通过指针重新分配 tryToChangeMe 变量。像这样:
auto temp = myClass.test();
*temp = 100;
如我所料,由于我在尝试为 const 变量赋值时发生的错误,程序无法编译。但是,我不只是 return 一个指向 const 的指针,我 returned 一个 const 指向 const(至少我是这么认为的)。因此,为了对此进行测试,我尝试将指针重新分配给一个新的内存地址,我确信我会遇到类似的编译错误:
temp = new int;
但令人困惑的是程序编译没有任何问题。通过调试器逐步显示,果然,指针丢失了它的原始地址并被分配了一个全新的地址。想知道发生了什么,我只是碰巧删除了 auto 关键字并将其替换为变量的完整类型:
const int* const temp = myClass.test();
再次测试所有内容后,结果符合预期,这次我无法将指针重新分配到新地址。
毕竟我想我的问题是,为什么?为什么 auto 关键字允许您绕过指针的 const 限定符?我是不是做错了什么?
顺便说一下,我不确定这是否重要,但我使用的是 Visual Studio 2015 预览版
原因是auto
个变量不是默认const
。您 return const
值的事实并不意味着它必须分配给 const
变量;该值毕竟是 copied(尽管该值是一个指针)。您也可以使用显式类型规范轻松尝试。当更改变量 temp
时,存储在 myClass
中的值不会更改,指针目标仍然是 const
,所以常量性仍然受到尊重。
写的时候
auto temp = rhs;
类型推导如下:
如果rhs
是引用,则忽略引用
top-level cv(const-volatile)-qualifiers of rhs
也被忽略(它们不会被忽略,但是如果你这样做auto& temp = rhs;
; 在这种情况下,编译器模式匹配类型)
在你的例子中,右侧的类型是
const int* const
^^^^^
top-level cv qualifier
即const
指向 const
-int
的指针。指针就像任何其他变量一样,所以它的 const
-ness 将被丢弃(从技术上讲,顶级 cv 限定符是 const
并且它被丢弃),因此你最终得到 temp
被推断为
const int*
即指向 const
-int
的非 const
指针,因此可以重新分配。如果你想强制执行 const
-ness,那么你必须将左侧声明为
const auto temp = myClass.test();
^^^^^
need this
Scott Meyers 有一篇 excellent introduction to the subject (also available on his Effective Modern C++ book, Items 1 and 2 free to browse here),他在其中解释了 template
类型推导的工作原理。一旦你理解了这一点,理解 auto
就轻而易举了,因为 auto
类型推导实际上非常接近模板类型推导系统(std::initializer_list<>
是一个明显的例外)。
编辑
还有一条附加规则
auto&& temp = rhs;
但要理解它,您需要理解 how forwarding (universal) references work and how reference collapsing works。
如前所述,auto
忽略顶级 cv 限定符。阅读 this article 了解 auto
和 decltype
工作原理的详细信息。
现在,即使 auto
没有忽略 const
,在你的情况下,temp
仍然不会是 const
,因为顶级 cv-qualifiers return 类型是 ignored 如果被 returned 的类型是非 class 类型。
g++ 甚至使用 -Wextra
产生以下警告
warning: type qualifiers ignored on function return type [-Wignored-qualifiers]
这可以通过使用 C++14 的 decltype(auto)
来证明。与 auto
不同,decltype(auto)
不会丢弃引用和顶级 cv 限定符。如果您通过添加以下行修改您的示例,代码仍将编译,证明 temp
不是 const
指针。
decltype(auto) temp = myClass.test();
static_assert(std::is_same<const int*, decltype(temp)>{}, "");
另一方面,如果 test()
return 是具有顶级 cv 限定符的 class 类型的对象,那么 auto
仍会丢弃 const
,但 decltype(auto)
不会。
我将为标准参考搜索者提供来自标准的一些正式解释。:
节N4296::7.1.6.4/7 [dcl.spec.auto]
If the placeholder is the auto type-specifier, the deduced type is
determined using the rules for template argument deduction.
现在,模板参数推导 N4296::14.8.2/3 [temp.deduct]
:
[...] the function parameter type adjustments described in 8.3.5 are
performed.
最后 N4296::8.3.5/5 [dcl.fct]
After determining the type of each parameter, any parameter of type
“array of T” or “function returning T” is adjusted to be “pointer to
T” or “pointer to function returning T,” respectively. After producing
the list of parameter types, any top-level cv-qualifiers modifying
a parameter type are deleted when forming the function type.
简而言之,是的,在这种情况下,CV 限定符将被忽略。
最近在学习C++,今天刚接触到const和const正确性的概念。为了更好地理解这个理论,我一直在编写一系列简单的程序来确保我正确理解了这个概念。我以为我明白了一切,但是当在其中一个程序中使用 auto 关键字时,我似乎有点卡住了。
为了测试我是否理解 const 指针的工作原理,我编写了一个简单的程序。我不会费心发布整件事,因为只有两部分是相关的。我有一个 class 和一个 int 类型的 const 数据成员:
const int tryToChangeMe;
在这个class中我还有一个成员函数return是一个指向上面的const int:
的const指针const int* const MyClass::test()
{
return &tryToChangeMe;
}
然后在我的主函数中调用上述函数,使用 auto 关键字。为了测试我认为我所知道的 const 是正确的,然后我尝试通过指针重新分配 tryToChangeMe 变量。像这样:
auto temp = myClass.test();
*temp = 100;
如我所料,由于我在尝试为 const 变量赋值时发生的错误,程序无法编译。但是,我不只是 return 一个指向 const 的指针,我 returned 一个 const 指向 const(至少我是这么认为的)。因此,为了对此进行测试,我尝试将指针重新分配给一个新的内存地址,我确信我会遇到类似的编译错误:
temp = new int;
但令人困惑的是程序编译没有任何问题。通过调试器逐步显示,果然,指针丢失了它的原始地址并被分配了一个全新的地址。想知道发生了什么,我只是碰巧删除了 auto 关键字并将其替换为变量的完整类型:
const int* const temp = myClass.test();
再次测试所有内容后,结果符合预期,这次我无法将指针重新分配到新地址。
毕竟我想我的问题是,为什么?为什么 auto 关键字允许您绕过指针的 const 限定符?我是不是做错了什么?
顺便说一下,我不确定这是否重要,但我使用的是 Visual Studio 2015 预览版
原因是auto
个变量不是默认const
。您 return const
值的事实并不意味着它必须分配给 const
变量;该值毕竟是 copied(尽管该值是一个指针)。您也可以使用显式类型规范轻松尝试。当更改变量 temp
时,存储在 myClass
中的值不会更改,指针目标仍然是 const
,所以常量性仍然受到尊重。
写的时候
auto temp = rhs;
类型推导如下:
如果
rhs
是引用,则忽略引用top-level cv(const-volatile)-qualifiers of
rhs
也被忽略(它们不会被忽略,但是如果你这样做auto& temp = rhs;
; 在这种情况下,编译器模式匹配类型)
在你的例子中,右侧的类型是
const int* const
^^^^^
top-level cv qualifier
即const
指向 const
-int
的指针。指针就像任何其他变量一样,所以它的 const
-ness 将被丢弃(从技术上讲,顶级 cv 限定符是 const
并且它被丢弃),因此你最终得到 temp
被推断为
const int*
即指向 const
-int
的非 const
指针,因此可以重新分配。如果你想强制执行 const
-ness,那么你必须将左侧声明为
const auto temp = myClass.test();
^^^^^
need this
Scott Meyers 有一篇 excellent introduction to the subject (also available on his Effective Modern C++ book, Items 1 and 2 free to browse here),他在其中解释了 template
类型推导的工作原理。一旦你理解了这一点,理解 auto
就轻而易举了,因为 auto
类型推导实际上非常接近模板类型推导系统(std::initializer_list<>
是一个明显的例外)。
编辑
还有一条附加规则auto&& temp = rhs;
但要理解它,您需要理解 how forwarding (universal) references work and how reference collapsing works。
如前所述,auto
忽略顶级 cv 限定符。阅读 this article 了解 auto
和 decltype
工作原理的详细信息。
现在,即使 auto
没有忽略 const
,在你的情况下,temp
仍然不会是 const
,因为顶级 cv-qualifiers return 类型是 ignored 如果被 returned 的类型是非 class 类型。
g++ 甚至使用 -Wextra
warning: type qualifiers ignored on function return type [-Wignored-qualifiers]
这可以通过使用 C++14 的 decltype(auto)
来证明。与 auto
不同,decltype(auto)
不会丢弃引用和顶级 cv 限定符。如果您通过添加以下行修改您的示例,代码仍将编译,证明 temp
不是 const
指针。
decltype(auto) temp = myClass.test();
static_assert(std::is_same<const int*, decltype(temp)>{}, "");
另一方面,如果 test()
return 是具有顶级 cv 限定符的 class 类型的对象,那么 auto
仍会丢弃 const
,但 decltype(auto)
不会。
我将为标准参考搜索者提供来自标准的一些正式解释。:
节N4296::7.1.6.4/7 [dcl.spec.auto]
If the placeholder is the auto type-specifier, the deduced type is determined using the rules for template argument deduction.
现在,模板参数推导 N4296::14.8.2/3 [temp.deduct]
:
[...] the function parameter type adjustments described in 8.3.5 are performed.
最后 N4296::8.3.5/5 [dcl.fct]
After determining the type of each parameter, any parameter of type “array of T” or “function returning T” is adjusted to be “pointer to T” or “pointer to function returning T,” respectively. After producing the list of parameter types, any top-level cv-qualifiers modifying a parameter type are deleted when forming the function type.
简而言之,是的,在这种情况下,CV 限定符将被忽略。