将指针(T*)转换为双常量指针(T const * const)。

3

C++

我想知道一个指针是否可以被隐式或显式地转换为 T const * const,即使它不是已经是一个两个const指针(例如T const * const)。在被用于初始化声明为 T const * const 的变量之前或之后,该指针是否可以通过某些方式(例如函数)进行处理或转换。我该怎么做?

我认为如果我从一个 T* 开始,那么一次 const_cast(或两次,在情况下只能每次转换一个 const)就足够了,但显然并不是这样的。代码中的许多变量都显示了不同的失败尝试,试图通过转换或从函数返回来产生一个带有尾随 constT const * const。每次转换都无法返回一个带有尾随 const 的指针。(我将 * 左边的 const 称为前导 const,右边的称为尾随 const。)由于转换失败,我尝试通过直接初始化强制使用 const。这是在 VC11 中编译的。在 stack-crooked.com 上的 g++ 给出了逻辑上等效的控制台输出,尽管对于 typeid(/*...*/).name() 使用了不同的名称。

#include <iostream>
#include <typeinfo>
using namespace std;

int const * const foo()
{
    return nullptr;
}

int main()
{
    int x = 7;

    auto a1 = &x;
    cout << typeid(a1).name() << endl;

    auto a2 = const_cast<int const *>(&x);
    cout << typeid(a2).name() << endl;

    auto a3 = const_cast<int * const>(&x);
    cout << typeid(a3).name() << endl;

    auto a4 = const_cast<int const * const>(&x);
    cout << typeid(a4).name() << endl;

    auto a5 = const_cast<int const * const>(a4);
    cout << typeid(a5).name() << endl;

    auto a6 = (int const * const) &x;
    cout << typeid(a6).name() << endl;

    auto a7 = static_cast<int const * const>(a4);
    cout << typeid(a7).name() << endl;

    auto a8 = reinterpret_cast<int const * const>(a4);
    cout << typeid(a8).name() << endl;

    auto a9 = foo();
    cout << typeid(a9).name() << endl;

    int const * const a10 = &x;
    cout << typeid(a10).name() << endl;
    cout << ( typeid(a10) == typeid(a4) ) << endl;

    auto a12 = a10;
    cout << typeid(a12).name() << endl;
    cout << ( typeid(a12) == typeid(a4) ) << endl;
}

预期结果与实际结果,以及问题:

问题编号对应相同编号的a#变量。

  1. 得到了预期结果 int *
  2. 得到了预期结果 int const *
  3. 期望的是 int* const,但得到的是 int*。为什么 const_cast 忽略了其尾随的 const 参数?由于返回值与参数具有相同的常量性和类型,那么转换是否运行?
  4. 期望的是 int const * const,但得到的是 int const*。为什么 const_cast 忽略了其尾随的 const 参数?
  5. 我想看看 const_cast<int const * const> 是否会在结果中包含尾随的 const,因为参数 a4 已经具有前导的 const。期望的是 int const * const。得到了 int const*。为什么 const_cast 忽略了其尾随的 const 参数?
  6. 期望的是 int const * const。得到了 int const*。为什么显式转换仍然排除了尾随的 const
  7. 期望的是 int const * const。得到了 int const*。为什么 static_cast 排除了尾随的 const
  8. 期望的是 int const * const。得到了 int const*。为什么 reinterpret_cast 排除了尾随的 const
  9. 期望从函数返回的 int const * const 的初始化结果仍然排除结果中的尾随 const。得到了 int const*
  10. 从控制台输出中得到了 int const*,但从调试器中没有得到 int const * const。变量 a10 明确声明为 int const * const,那么为什么 typeid().name() 排除了尾随的 const?operator== 得到了 1,那么为什么对于 a10typeid() 本身(不仅仅是名称)等价于 a4 的类型标识符?VC11 调试器将 a10 的类型列为 int const * const。为什么与 typeid()typeid().name() 的结果不同?哪一个是正确的?
  11. 变量名 a11 被省略,因为它看起来像单词 “all”。
  12. 我期望 a12int const * const,因为它被初始化为 a10,后者明确声明为 int const * constoperator== 得到了 1,那么 typeid() 仍然是 int const*。从控制台输出和调试器中都得到了 int const*。为什么它们与预期结果不同?
所有类型转换、函数返回和初始化都只能一��性转换一个 const 吗?转换的唯一限制是前面的 const 吗?
1个回答

3

const_cast 并不像你想象的那样工作。然而,auto 也不是你想象的那样。 auto 的工作方式类似于函数模板参数推导(实际上是基于后者定义的)。现在考虑:

template<typename T>
void f(T x);

f(42);
int const n = 42;
f(n);

这两个调用都是针对 f<int>(),而不是 f<const int>()。顶层 const 修饰符会被模板参数推导忽略。出于同样的原因,在这个例子中

auto a = 42; a = 84;
auto b = n;  b = 84;

ab变量的类型是int,而不是const int,可以被修改。


谢谢你的答案;它澄清了许多问题,但您是否有关于#10的答案?此外,您是说前导const不是顶层const修饰符;只有尾随的const是吗?由于auto会忽略顶层的const修饰符,所以我如何在失去顶层const修饰符之前观察或者最好是保存每次转换的结果?const_cast<T const * const>(&t_type)可以将T*直接强制转换为T const * const,还是我需要两个强制转换,每个const都需要一个? - CodeBricks
1
顶层cv限定符始终被忽略,无论是作为glvalue表达式的操作数还是作为typeid运算符的操作数的类型标识。 - Igor Tandetnik
1
适用于正在声明的变量的 const 是顶层 const 修饰符,无论它在开始还是结尾词法上出现。在 const int n = 42int const n = 42 中,const 修饰符都是顶层的。在 const int* const! pint const * const! ptypedef const int* CP; const! CP p 中,我用感叹号标记了顶层限定符。 - Igor Tandetnik
你的回复非常有帮助,但是你是否有回答我在之前评论中提出的最后两个问题?如果你的回答和评论已经适用,那么很抱歉我的标准语表达能力不好。 - CodeBricks
目标:查看包括所有const限定符的const_cast返回值。只要我找到了,就不需要观察顶层const修饰符。从您的回答中:“const_cast确实按您所想的方式工作。” 我想更多地澄清,因为我不知道它确切返回什么; OP问是否可以在顶层const中进行强制转换以及需要多少次强制转换才能将顶层和非顶层const都进行转换。尽管OP没有说要从左值转换指针,但这就是我的意思。并非OP中的每个转换都来自右值(例如a4)。尝试从标准中找到答案,但有些部分存在疑问。 - CodeBricks
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接