c++ - 当其中之一是结构时,C++ 类函数参数语法有什么不同?
问题描述
在编写新的类函数时,我通常可以安全地将函数定义的第一行(从源文件中)复制到类头以添加声明,使其成为类的一部分。我通常不使用 C++ 中的结构,但现在有了,我遇到了这样一种情况,即在定义的参数列表中指定结构似乎有一些特殊情况。该结构是同一类的数据成员。
头文件中的函数定义:
int freq_sort(unsigned char* source, struct freq_pair* target);
源文件中该函数定义的第一行:
int TargaImage::freq_sort(unsigned char* source, struct freq_pair* target){
关于声明的编译器错误:
TargaImage.cpp:324:5: error: no declaration matches ‘int TargaImage::freq_sort(unsigned char*, TargaImage::freq_pair*)’
324 | int TargaImage::freq_sort(unsigned char* source, struct freq_pair* target){
| ^~~~~~~~~~
编译器错误提示正确定义:
TargaImage.h:96:13: note: candidate is: ‘int TargaImage::freq_sort(unsigned char*, freq_pair*)’
96 | int freq_sort(unsigned char* source, struct freq_pair* target);
| ^~~~~~~~~
结构数据成员:
struct freq_pair {
unsigned char val;
int count;
};
我看到编译器看到的主要区别是定义中的 struct 参数而不是声明中使用的范围解析运算符。我不明白为什么会这样。这里发生了什么?
我尝试将类范围添加到定义、声明或两者中的 struct 参数,以哄骗它工作但无济于事。即使确实如此,我也不明白问题是什么。
在这里找到问题的下一步是什么?
谢谢,
特伦特
编辑1:
这是原始问题的最小可重现代码,从评论中获得的见解没有任何变化。
class TargaImage
{
public:
//function
int freq_sort(unsigned char* source, struct freq_pair* target);
//data member
struct freq_pair {
unsigned char val;
int count;
};
};
int TargaImage::freq_sort(unsigned char* source, struct freq_pair* target){
return 0;
}
int main(){
TargaImage obj;
return 0;
}
现在我明白了以与类类似的方式来考虑结构。所以,如果我本质上是在我的原始类中定义一个新的“类”(结构),那么包含该类以供使用的规则必须不同于通常的预处理器指令。我认为由于新的 freq_pair“类”(结构)在原始 TargaImage 类中,因此它的定义在 TargaImage 成员函数的范围内。因此,从参数中删除 struct 关键字。但我得到了错误:
main.cpp:6:42: error: ‘freq_pair’ has not been declared
6 | int freq_sort(unsigned char* source, freq_pair* target);
|
所以我的结论是 freq_pair 超出了 TargaImage 的范围,尽管它是在类中定义的。这个结论正确吗?我应该如何通知 TargaImage::freq_sort(...) 类类型 freq_pair?
我认为将 C 风格的结构添加为数据成员是一个好主意,但主要的收获可能是在其他地方定义一个类,该类包含该结构所持有的一对数据成员。
解决方案
C++是从源代码自上而下编译的。除了在模板中,每次遇到标识符时,都会执行名称查找。这意味着编译器试图在源代码的前一部分中找到标识符的可达声明。
这里你freq_pair
第一次在函数参数里面使用标识符
int freq_sort(unsigned char* source, struct freq_pair* target);
因为它没有事先声明,编译器还不知道freq_pair
应该是什么。通常这会导致一个错误说freq_pair
是未声明的。然而struct
关键字基本上告诉编译器:“freq_pair
是一个类类型,如果你没有找到这样的类类型,那么在这里声明它。 ”
因此freq_pair
将被声明,但问题是它将被声明的确切位置(即在哪个范围内)。它可以在内部声明为嵌套类(struct
并且class
都引入类,在 C++ 中,两者在类型标识方面没有区别)TargaImage
,作为函数的局部类或全局类。事实上,后者就是这种情况,正如C++17 标准(草案 N4659)的[basic.scope.pdecl]/7.2所指定的(另请参阅此问题以了解类似情况):
对于形式的详细类型说明符
类键标识符
如果 [...]; 否则,除非作为友元声明,否则标识符将在包含该声明的最小命名空间或块范围内声明。
详细类型说明符是一种类型说明符,它使用struct
或其他类键关键字之一,即正是您所拥有的struct freq_pair
. 您的函数声明在类范围内(既不是命名空间范围也不是块范围),因此freq_pair
不能将声明放置在那里。包含的下一个最小范围class TargaImage {...};
是全局范围,它被认为是命名空间范围。因此这条线
int freq_sort(unsigned char* source, struct freq_pair* target);
声明一个全局struct freq_pair
,声明中引用的类型就是该类型。
然后
struct freq_pair {
unsigned char val;
int count;
};
定义一个freq_pair
嵌套在类中的类TargaImage
。这与您事先声明的全局类不同。
然后我们来定义
int TargaImage::freq_sort(unsigned char* source, struct freq_pair* target){
return 0;
}
在这里,因为我们定义了一个函数,它是 的一部分,所以首先在里面查找TargaImage
名称,现在我们看到声明了一个嵌套 inside的定义,即。如果可以找到与名称匹配的类型,则关键字不会产生任何进一步的影响,因此在此定义中现在指的是.freq_pair
TargaImage
struct freq_pair {...};
freq_pair
TargaImage
TargaImage::freq_pair
struct
freq_pair
TargaImage::freq_pair
结果,您声明了一个带有指向全局指针的成员函数::freq_pair
,但试图定义一个带有指向嵌套指针的成员函数::TargaImage::freq_pair
。编译器抱怨这些不匹配。
要解决此问题,请删除struct
变量声明中的所有关键字,并将其仅用于定义或显式前向声明类。如您所见,将其用作详细的类型说明符只会让人头疼。class
相同的规则适用于其他详细说明的类型说明符,即那些以/ enum
/开头的说明符union
。
但是,这样做会导致错误,因为freq_pair
在我上面解释的成员声明中找不到。这很容易通过移动freq_pair
使用点之前的定义来解决:
class TargaImage
{
public:
//data member
struct freq_pair {
unsigned char val;
int count;
};
//function
int freq_sort(unsigned char* source, freq_pair* target);
};
int TargaImage::freq_sort(unsigned char* source, freq_pair* target){
return 0;
}
如果由于某种原因无法做到这一点,那么您可以使用前向声明来确保第一次查找找到正确的类型(即使此时它不完整):
class TargaImage
{
public:
//explicit forward declaration
struct freq_pair;
//function
int freq_sort(unsigned char* source, freq_pair* target);
//data member
struct freq_pair {
unsigned char val;
int count;
};
};
int TargaImage::freq_sort(unsigned char* source, freq_pair* target){
return 0;
}
另请注意,这freq_pair
是一个嵌套类,而不是数据成员。
推荐阅读
- scala - Scala Option 获取键值
- java - 例外:期待菜单,得到 androidx.constraintlayout.widget.ContstraintLayout
- sql - SQL 创建三个表并与之交互
- amazon-web-services - ECS 可以在单独的帐户中访问 ECR
- html - 如何为网格中的各个列着色
- image - 如何将rois提取为induvidual roi图像
- docker - 从 k3s-master 在 k3s-agent 上运行 docker 容器
- go - 如何为不同的 gRPC 端点重用 proto-buffer 消息?
- graphql - 将标头转发到实现
- javascript - 确保在回调后触发异步方法