typedef const char* 与 const typedef char*

typedef const char* vs. const typedef char*

提问人:vdavid 提问时间:4/5/2023 更新时间:4/5/2023 访问量:197

问:

我注意到 typedef 指针上的 constness 失去了将其隐式转换为 un-typedef'ed 类型的 const 类型的能力。

由于我显然缺乏适当的词汇来解释这个问题,我将展示一些例子。

以下代码无法使用 GCC 8.3.0 编译

typedef char* char_ptr;
void myFunc(const char_ptr param);
void test() {
  const char str[] = "Hello, World!";
  myFunc(str);
}

它给出以下错误:

错误:从“const char*”到“char_ptr”{又名“char*”} [-fpermissive] myFunc(str);

我不是在寻找解决方法。我知道很多方法可以解决这个问题,例如强制转换类型或更改函数声明。

我想了解为什么我会遇到这个错误,以及为什么它只发生在 typedef 指针类型的 const 上。

我缩小了一些用例的范围,试图理解编译器何时认为类型是相同的 https://ideone.com/Rk9gD9

typedef char char_t; // Char type
typedef char* char_ptr; // Char pointer
typedef const char* char_const_ptr; // Char const pointer

IS_SAME( char, char );                  // Obviously true
IS_SAME( char, float );                 // Obviously false

// Testing char_t
IS_SAME( char, char_t );                // true: OK
IS_SAME( const char, char_t );          // false: OK
IS_SAME( char, const char_t );          // false: OK
IS_SAME( const char, const char_t );    // true: OK
    
// Testing char_ptr
IS_SAME( char*, char_ptr );             // true: OK
IS_SAME( const char*, char_ptr );       // false: OK
IS_SAME( char*, const char_ptr );       // false: OK
IS_SAME( const char*, const char_ptr ); // false: Why?
IS_SAME( char* const, char_ptr );       // false: OK
IS_SAME( char* const, const char_ptr ); // true: Why?

// Testing char_const_ptr
IS_SAME( char*, char_const_ptr );       // false: OK
IS_SAME( const char*, char_const_ptr ); // true: OK
IS_SAME( char* const, char_const_ptr ); // false: OK

我可以正确预测所有情况,除了那些与我预期相反的情况。is_same<const char*, const char_ptr>is_same<char* const, const char_ptr>

我对这个 SO 主题的理解是,在 typedef 指针类型之前写入实际上等同于在 untypedef 指针类型之后写入(这与上述测试的结果相匹配)。我似乎无法与一种良好的记忆技术联系起来,以提醒自己将来何时应该在 typedef'ed 或 untypedef'ed 类型之前或之后编写 const 类型限定符。constconst

c++ char 参数传递 typedef is-same

评论

3赞 Drew Dormann 4/5/2023
const char_ptr表示 ,而不是 。您正在使指针 const。char* constconst char*
1赞 user4581301 4/5/2023
Clang 给出了一个更好的诊断:godbolt.org/z/qWhKTsv6v
2赞 Jesper Juhl 4/5/2023
旁注:在现代 C++ 中,首选 .前者可以做后者能做的一切——甚至更多。这些天没有理由使用.usingtypedeftypedef
0赞 Den-Jason 4/5/2023
这就是为什么我更喜欢使用从右到左的规则
0赞 Drew Dormann 4/5/2023
这是练习东常的一个很好的理由。 方法。char_ptr constchar* const

答:

1赞 Ted Lyngmo 4/5/2023 #1

我将用以下代码(传递静态断言)来说明 和 添加的内容:typedefconst

#include <type_traits>

typedef char* char_ptr;

static_assert(std::is_same_v<const char_ptr, char* const>);
// which is the same as
static_assert(std::is_same_v<char_ptr const, char* const>);

const适用于指针 - 而不是它所指向的指针。 总是适用于它剩下的东西 - 除非左边什么都没有,否则它适用于它的右边,但它通过做 来应用于整个 as-ifcharconstchar_ptrchar_ptr const

1赞 Vlad from Moscow 4/5/2023 #2

本声明:typedef

typedef char* char_ptr;

将名称声明为 类型的别名。也就是说,作为指向非常量的指针的别名。char_ptrchar *char

所以,这个函数声明:

void myFunc(const char_ptr param);

将其参数声明为指向非常量的常量指针。paramchar

它可以被重写为:

void myFunc( char * const param);

注意这一点。为了确定函数的类型,将放弃高级限定符。也就是说,上面的函数声明等价于下面的声明(即不是函数定义):conts

void myFunc( char * param);

例如,您可以声明如下函数:

void myFunc( char * param);

并定义它,例如:

void myFunc( char * const param)
{
    std::cout << param << '\n';
}

反之亦然,您可以声明如下函数:

void myFunc( char * const param);

并像这样定义它:

void myFunc( char * param)
{
    std::cout << param << '\n';
}

在第一个函数定义中,您不能更改指针本身,例如:

std::cout << ++param << '\n';

但是,在这两个函数定义中,您可以更改指针指向的字符,例如:

*param = 'A';

因为指针,无论它本身是否是常量指针,都是指向非常量字符的指针。

另一方面,在函数中,您声明了一个常量字符数组:test()

const char str[] = "Hello, World!";

使用数组作为参数表达式,它被隐式转换为指向其第一个元素的指针,类型为 。const char *

并且,您正在尝试将此类型的指针传递给函数,该函数的相应参数具有 .也就是说,参数表达式的类型指针指向常量对象,而参数具有指向非常量对象的类型常量指针。const char *char * const

因此,编译器发出错误。

您可以像这样声明 typedef:

typedef const char* char_ptr;

然后函数声明:

void myFunc(const char_ptr param);

将等同于:

void myFunc(const char * const param);

您可以从以下函数调用该函数:test()

void test() {
    const char str[] = "Hello, World!";
    myFunc(str);
}
1赞 Martin York 4/5/2023 #3

你必须记住,这不是一个马克罗。typedef

您的解释似乎认为您可以在文本上将 typedef(别名)替换到代码中,并且它是一样的。事实并非如此。它创建一个新的类型别名(可以把它想象成在类型周围添加括号)。

typedef char char_t; // Char type
typedef char* char_ptr; // Char pointer
typedef const char* char_const_ptr; // Char const pointer

要记住的另一条规则是束缚左边。除非它在最左边,否则它会绑定右边。因此,将常量从“最左边”向右移动会得到相同的类型。const

我总是将“最左边”移开,因为它使阅读类型更容易(从右到左阅读)。要进行一致的比较,您应该这样做,因为您现在有一个标准位置,您可以进行文本比较。constconst

因此,我会将您的类型视为:

char_t         =>  (char)
char_ptr       =>  (char*)
char_const_ptr =>  (const char*)   => (char const *)

现在看看你的问题:

IS_SAME( const char*, const char_ptr ); // false: Why?
IS_SAME( char* const, const char_ptr ); // true: Why?

const char*         =>                                          char const *
char* const         =>                                          char * const


const char_ptr      => const (char*)    => (char*) const     => char * const

查看阅读类型

评论

0赞 vdavid 4/5/2023
"const绑定左。除非它在最左边,否则它就会束缚在右边“是一种非常有趣的看待它的方式。谢谢。这也让我想知道为什么 C 标准允许这种令人困惑的符号。