首页 > 解决方案 > 当其中之一是结构时,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++struct

解决方案


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_pairTargaImagestruct freq_pair {...};freq_pairTargaImageTargaImage::freq_pairstructfreq_pairTargaImage::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是一个嵌套类,而不是数据成员


推荐阅读