typedef const char* vs. const typedef char*



我注意到一个类型定义的指针上的constness失去了隐式转换为非类型定义类型的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);

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

我想了解为什么我得到这个错误,为什么它只发生在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>

我对这个主题的理解是,在之前写const一个类型定义的指针类型实际上相当于在之后写const一个非类型定义的指针类型(匹配上述测试的结果)。我似乎没有一种好的记忆技巧来提醒自己,什么时候const类型限定符应该写在类型定义或非类型定义的类型之前或之后。

我将用下面的代码(传递静态断言)来说明typedef和添加的const发生了什么:

#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适用于指针,而不是它所指向的charconst总是应用于它的左边——除非左边没有任何东西,那么它就应用于它的右边,但是它应用于整个char_ptr,就像通过执行char_ptr const一样。

typedef声明:

typedef char* char_ptr;

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

那么,这个函数声明:

void myFunc(const char_ptr param);

将形参param声明为指向非常量char的常量指针。

可以重写为:

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 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);
}

您必须记住typedef不是宏。

你的解释似乎认为你可以把typedef(别名)文本地替换成代码,它是一样的。这不是真的。它创建了一个新的类型别名(就像在类型周围添加括号一样)。

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

另一个要记住的规则是const绑定左。除非它在最左边,否则它会与右边结合。所以把const从最左边移开再往右一个位置是相同的类型

我总是把const从"very left"因为它使阅读类型更容易(类型从右向左阅读)。为了进行一致的比较,您应该这样做,因为您现在有了const的标准位置,并且可以进行文本比较。

所以我会把你的类型看成:

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

参见阅读类型

相关内容

  • 没有找到相关文章

最新更新