【问题标题】:how to create a forward declaration of a typedef struct如何创建 typedef 结构的前向声明
【发布时间】:2018-09-18 13:37:33
【问题描述】:

我有 1 个 .h 文件 test.h 包含一个类。在这个类中,有一个私有方法返回一个我不想“公开”的类型的指针,但我希望能够将这个 test.h 文件包含在其他源文件中。

一般来说,在 .h 文件中使用前向声明很容易:

class Foo;

但问题是这种类型来自一个 C 文件我无法更改(因为它是我没有维护的其他代码)并且它是一个 typedef

所以基本上我的test.cpp 是:

// this type comes from a C file, cannot be changed to struct or class
typedef struct 
{
   int x;
} Foo;

#include "test.h"

static Foo foo;

Foo *Test::private_function()
{
  foo.x = 12;
  return &foo;
}

int Test::compute()
{
   auto *local_foo = private_function();
   return local_foo->x;
}

而我的test.h 文件是:

#pragma once

struct Foo;

class Test
{
public:
  Test() {}
  int compute();
private:
  Foo *private_function();
};

尝试编译失败:

>g++ -std=c++11 -c test.cpp
In file included from test.cpp:10:0:
test.h:3:8: error: using typedef-name 'Foo' after 'struct'
test.cpp:7:3: note: 'Foo' has a previous declaration here

目前我的解决方法是返回void * 并来回执行static_cast,但我认为这不是最佳的。有更好的解决方案吗?

(我已经检查了Forward declaration of a typedef in C++,但我测试了解决方案,但它们似乎不适用于我的情况,也许我想做的更简单/不同 - 我只有一个 .h 和 .cpp - 或者只是不可能)

【问题讨论】:

  • 很遗憾,typedefs 无法前向声明。一种常见的解决方法是拥有一个从 C 结构继承的 C++ 类,由其 typedef 引用,您可以前向声明它。这将需要一些代码更改,但它们应该是最小的。
  • @SamVarshavchik 看起来像是一个有效的答案。我刚刚在我的示例 (class FooC : public Foo {};) 上尝试过,它成功了。 Tme 在我的真实案例中进行测试。非常感谢您:) 发布您的答案并收集 25+
  • 这并没有解决问题,但请注意 extern "C" 周围的 stuct 定义没有做任何事情。那里没有任何外部联系。 extern "C" 并不意味着“将其编译为 C 代码”;它的意思是“生成可以与我的本地 C 编译器生成的代码链接的目标代码”。

标签: c++ forward-declaration


【解决方案1】:

返回这个:

//#include "SecretFoo.h"
struct SecretFoo {
  uintptr_t handle;
};

//#include "SecretFooImpl.h"
#include "SecretFoo.h"
#include "Foo.h" // definition of typedef struct {int x;} Foo;
Foo* Crack( SecretFoo foo ) {
  return reinterpret_cast<Foo*>(foo.handle);
}
SecretFoo Encase( Foo* foo ) {
  return {reinterpret_cast<uintptr_t>(foo)};
}

现在我们得到:

#include "SecretFooImpl.h"
static Foo foo;

SecretFoo Test::private_function()
{
  foo.x = 12;
  return Encase(&foo);
}

int Test::compute()
{
   auto *local_foo = Crack(private_function());
   return local_foo->x;
}

在你的标题中:

#pragma once
#include "SecretFoo.h"

class Test
{
public:
  Test() {}
  int compute();
private:
  SecretFoo private_function();
};

这归结为相同的二进制代码,但SecretFoo 和配对的Crack/Encase 函数提供了一种比void* 更安全的类型转换。


这种技术有时在 C 世界中使用。 SecretFoo 是句柄的一种;一个不透明的类指针结构。在这种情况下,其中的数据(uintptr_t handle)只是一个强制转换指针;但它可能是指向指针表或其他任何东西的指针。 CrackEncase 方法是允许访问/创建 SecretFoo 的唯一方法。

【讨论】:

  • 至少它有效。但对我来说仍然听起来很复杂。到/来自void * 的静态转换具有相同的效果并且不太复杂。
  • @Jean-FrançoisFabre 对static_cast&lt;Foo*&gt;(some_void_ptr) 的调用必须知道void* 的来源。这意味着充分了解程序中void*s 的数据流,以了解该行是否正确。像SecretFoo 这样的“强”void* 在一对函数中生成和消耗;他们需要相互验证,你就完成了。转换void*s 的代码库既危险又脆弱,因为它们没有类型安全性;像SecretFoo 这样的类型化句柄非常安全。但是,是的,在一个拥有完美程序员的小项目中,您可以使用void*
【解决方案2】:

很遗憾,typedef 不能前向声明。

一种常见的解决方法是拥有一个从 C 结构继承的 C++ 类,由其 typedef 引用,您可以前向声明它。这将需要一些代码更改,但应该是最少的。

(代表https://stackoverflow.com/users/3943312/sam-varshavchik发表评论)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-19
    • 1970-01-01
    相关资源
    最近更新 更多