使用指向 C++ 中临时静态分配对象的引用/指针初始化多态 C 数组 [已关闭]

Initialize polymorphic C array with references/pointers to temporary statically allocated objects in C++ [closed]

提问人:T. R. Bernstein 提问时间:9/19/2023 最后编辑:user17732522T. R. Bernstein 更新时间:9/19/2023 访问量:48

问:


想改进这个问题吗?通过编辑这篇文章添加详细信息并澄清问题。

2个月前关闭。

问题

是否可以使用指向 C++ 中临时对象的引用/指针初始化多态 C 数组(例如 ParentClass* 类型的数组,包含 Subclass1*、Subclass2* 等类型的指针)? 由于在编译时都是已知的,我不想使用或任何具有动态分配的东西,例如使用 .temporary objectsunique_ptrnew

用例

目前版本

鉴于

class Command {
 public:
  virtual void run(const vector<string>& args) = 0;
};

class Generate: public Command {
 public:
  void run(const vector<string>& args) override;
};

class Scan: public Command {
 public:
  void run(const vector<string>& args) override;
};

我想定义一个包含数据成员的类,例如:Appstatic Command* commands[]

// App.hpp
class App {
  static Command* commands[];
}

// App.cpp
Generate generate;
Scan scan;
Command* App::commands[] {&generate, &scan};

编译器错误

在尝试寻找有效的解决方案时,我遇到了以下编译器错误列表:

taking the address of a temporary object of type 'Generate':
  Command* App::commands[] {&Generate()};`
non-const lvalue reference to type 'Generate' cannot bind to a temporary of type 'Generate'
  Command* App::commands[] {&static_cast<Generate&>(Generate())};
cannot initialize an array element of type 'Command *' with an rvalue of type 'const Generate *'
  Command* App::commands[] {&static_cast<const Generate&>(Generate())};

编译器:。g++: Apple clang version 15.0.0 (clang-1500.0.29.1)

寻求的改变

它希望删除全局变量并改用临时对象,例如:App.cppgeneratescan

// App.cpp
Command* App::commands[] {&Generate(), &Scan()};

更好的是,直接在 中去掉和定义,例如:App.cppcommandsApp.hpp

// App.hpp
class App {
  static Command* commands[] {&Generate(), &Scan};
}

// App.cpp is deleted
C++ 数组 C++17 多态性 临时对象

评论

0赞 Sam Varshavchik 9/19/2023
它们之所以被称为“临时”,是因为它们很快就会消失。一旦数组被初始化,它们就会被销毁。即使这是可能的,你也会留下一个指向被摧毁物体的数组,以任何方式使用它们会立即释放出一大群恶魔从每个人的鼻子里飞出来。你喜欢从每个人的鼻子里飞出的恶魔吗?为什么?没有人喜欢这样。
0赞 paddy 9/19/2023
可能值得问问自己,这些命令是否需要实际存储任何状态。正如目前所写的,该方法是非常量的,这意味着 Command 对象可以修改自身。类设计还意味着某些命令可以继承其他命令,或者可以使用其他数据初始化 Command 对象。如果这些都无关紧要,你可以通过将命令转换为 lambda 来大大简化事情......甚至只是普通的功能。run
0赞 T. R. Bernstein 9/19/2023
@SamVarshavchik我明白了。但是临时对于初始化许多对象很有用。 通常由临时对象初始化。我曾希望在我的情况下也能做一些同样美好的事情。string
0赞 T. R. Bernstein 9/19/2023
@paddy 在那种特殊情况下,这是一个好主意。声明为 I 能够使用以下方法获得相同的行为: .但是,如果需要任何状态,则需要解决方案。runstaticinline static void(*commands[])(const vector<string>&) {Generate::run};

答:

0赞 user17732522 9/19/2023 #1

是的,但不是指针。它只能使用数组来聚合包含引用成员的类:

struct CommandRef {
    Command&& command;
};

/* DO NOT USE, SEE BELOW */

CommandRef App::commands[] {{Generate()}, {Scan()}};

但是,我强烈建议不要这样做,因为上述内容依赖于非常特殊的生存期扩展规则来绑定聚合初始化中的引用。

它很容易被错误地做出未定义的行为,尤其是在缺乏对正式生命周期规则的理解的情况下,例如

  • 创建非聚合类。CommandRef
  • 在聚合初始化中使用 / 而不是 /。(){}
  • 在实现临时值的 PR值表达式和引用的绑定之间应用任何操作。
  • 使用除内置数组或 .std::array

编译器(至少在过去)也存在错误,导致生存期扩展无法正确实现,因此实际上代码将再次具有未定义的行为。

取而代之的是,使用带有命名变量的原始方法,或者只是使用以下命令将对象直接放入数组中:std::variant

std::variant<Generate,Scan> App::commands[] {Generate(), Scan()};

更好的是,摆脱 App.cpp 并直接在 App.hpp 中定义命令

这是一个单独的问题,在 C++ 17 及更高版本中很容易:您只需要放入之前/之后,然后就可以在类中初始化静态数据成员,就像它在类外工作一样。inlinestatic