c++ - 是不是必须定义所有静态类方法,即使不使用?

Posted

技术标签:

【中文标题】c++ - 是不是必须定义所有静态类方法,即使不使用?【英文标题】:c++ - Must all static class methods be defined, even when not used?c++ - 是否必须定义所有静态类方法,即使不使用? 【发布时间】:2017-09-13 22:53:04 【问题描述】:

所以,我遇到了一个问题,我不确定是语言问题,还是编译器/GCC 问题。

TL;DR - 我是否需要定义类中的所有静态方法,即使这些静态方法从未被调用应用程序(即无论如何都可以被链接器合法地删除)?

我有一个库类,它为微控制器中的 UART 实现设备驱动程序。因为我不希望多个 UART 对象指向同一个资源,所以每个 UART 对象都是一个单例,使用几个 GetInstance() 方法之一检索,一个用于设备中的每个 UART 实例(UART0、UART1 等)。每个 UART 实例需要有两个 FIFO(Tx 和 Rx)用于存储。每个 FIFO 需要由 应用程序 明确地确定大小,并在 UART 对象被实例化时分配(理想情况下)。所以我也有几个静态的GetStorage() 方法,同样,每个UART 一次。

我为我的概念证明创建了一些精简代码。这是 static_instance.h:

#ifndef STATIC_INSTANCE_H_
#define STATIC_INSTANCE_H_

#ifdef __cplusplus
#include <vector>
namespace foo 
class Uart 
 public:
  /* Retrieve a singleton instance, using lazy static initialization.  Note
   * that not all instances will be present for a given device. */
  static Uart& Uart1GetInstance(void);
  static Uart& Uart2GetInstance(void);
  static Uart& Uart3GetInstance(void);
  /* Does something. */
  void DoSomething(void)  ++counter; 
 private:
  /* Structure for the storage that each static Uart instance requires. */
  struct Storage 
    Storage(std::vector<char>& vector)
        : my_vector_(vector)  
    std::vector<char>& my_vector_;   // Buffer for data.
  ;
  /* Instantiate object using provided register base and FIFO structures. */
  Uart(int instance, Storage& storage)
      : instance_(instance), storage_(storage)  
  ~Uart()  
  /* Retrieves the storage required for the static Uart object instances.
   * These methods are NOT implemented in static_instance.cc, but must be
   * implemented in the application code, only for those Uart instances
   * that are invoked in the application. */
  static Storage& Uart1GetStorage(void);
  static Storage& Uart2GetStorage(void);
  static Storage& Uart3GetStorage(void);
  int const instance_;    // Instance number of this object.
  Storage& storage_;      // Allocated storage for this object.
  int counter = 0;        // Dummy counter.
;
 // namespace foo
#endif  // __cplusplus

#endif

这里是 static_instance.cc:

#include <static_instance.h>
namespace foo 

Uart& Uart::Uart1GetInstance(void) 
  static Uart uart(1, Uart1GetStorage());
  return uart;

Uart& Uart::Uart2GetInstance(void) 
  static Uart uart(2, Uart2GetStorage());
  return uart;

Uart& Uart::Uart3GetInstance(void) 
  static Uart uart(3, Uart3GetStorage());
  return uart;


 // namespace foo

这个想法是您只为您实际需要的 UART 实例调用 GetInstance(),然后只为该 UART 实例定义 GetStorage()。 (对于这个例子,我只定义一个缓冲区,并使用std::vector&lt;char&gt; 作为替代。)此外,由应用程序定义存储方法,因为每个应用程序都有自己的要求给定 UART 的缓冲区需要很大。 (我绝对不会将宏放在我的 C++ 模块中,因为,ew。)这是 main.cc 中用于实例化 UART2 的代码 sn-p:

namespace foo 

Uart::Storage& Uart::Uart2GetStorage(void) 
  static std::vector<char> rx_vector(256, 0);
  static Uart::Storage storage(rx_vector);
  return storage;

static foo::Uart& uart_ = foo::Uart::Uart2GetInstance();
void wibble(void) 
  uart_.DoSomething();


 // namespace foo

现在,我正在使用该芯片的早期 IDE 开发一个早期应用程序(好奇的 Kinetis Design Studio v3.2.0),它使用 GCC 4.8.4 并与 进行编译和链接>没有错误

但 NXP 已弃用另一个工具链 (MCUXpresso 10.0) 的 KDS,该工具链使用 GCC 5.4.1,并且使用完全相同的代码,这次我得到 两个链接器错误

./source/static_instance.o: In function 'foo::Uart::Uart1GetInstance()':
../source/static_instance.cc:5: undefined reference to 'foo::Uart::Uart1GetStorage()'
./source/static_instance.o: In function 'foo::Uart::Uart3GetInstance()':
../source/static_instance.cc:13: undefined reference to 'foo::Uart::Uart3GetStorage()'

我不确定为什么链接器会关心未定义 UART1 和 UART3 的 GetStorage() 方法,因为我没有在我的应用程序中为 UART1 或 UART3 调用 GetInstance(),因此从不调用也可以对应GetStorage() 方法。

我的问题是...... C++11 要求我在我的可执行文件中定义所有三种存储方法吗?也就是说,GCC 4.8.4 是否让我摆脱了我不应该做的事情?或者这是我需要切换的一些 GCC 5.4 选项,以允许我从类中删除未使用的静态成员?

如果答案是“无论如何你都必须定义它们”,那么我会定义它们,或者可能设计一些其他方式来允许。如果答案是“那应该没问题”,并且我无法在命令行上设置让 GCC 5.4 执行此操作的选项,那么我将采取下一步并在 NXP 论坛中报告错误。谢谢。

【问题讨论】:

为什么不公开CreateInstance 函数而不是要求用户代码来定义(某些)函数? coliru.stacked-crooked.com/a/5e84854041391a9c @aschepler - 我喜欢这种模式。但是假设我有一个板对象,我想通过调用GetInstance(2) 来初始化Uart&amp; 引用。我如何保证CreateInstance(2) 将在该对象引用被初始化之前的某个时间被调用? 【参考方案1】:

TL;DR - 我是否需要在一个类中定义所有静态方法,即使这些静态方法从未被应用程序调用

您可能需要定义此类静态方法,即使它们从未被应用程序调用。

但是,一般来说,如果这些函数不是 odr-used(一个定义规则),您可能不需要。对于静态成员函数,这相当于

名称显示为潜在求值表达式的函数

区别很微妙。我希望这能证明这一点:

if(false)
    function();

function 永远不会被调用,但会出现在可能求值的表达式中,因此是 odr-used 的,因此必须定义。


我的问题是……C++11 是否要求我在我的可执行文件中定义所有三种存储方法?

是的。它们都出现在可能求值的表达式中,因此是 odr-used,因此必须定义。

我正在使用 ... GCC 4.8.4 开发早期应用程序,并且编译和链接没有错误。

Odr 违规行为未定义,这解释了为什么您在其他工具链中没有收到错误/警告。

【讨论】:

【参考方案2】:

[basic.def.odr]

    每个程序都应该包含一个对每个非内联函数或变量的定义 该程序在被丢弃的语句(9.4.1)之外;无需诊断。

这是一个相对较新的标准草案,但所有版本都包含类似的声明。

“不需要诊断”子句允许编译器接受您的程序,即使它违反了规则。在无法访问的代码段中使用 odr 正是合理的情况:编译器可能会或可能不会优化掉包含违规调用的死代码。您的程序仍然存在违规,另一个实现可能会拒绝它。

【讨论】:

以上是关于c++ - 是不是必须定义所有静态类方法,即使不使用?的主要内容,如果未能解决你的问题,请参考以下文章

C++中类里面定义 静态成员变量的问题

C++中的派生类,可以不定义对象直接调用基类的成员和调用自己的成员函数嘛???

Heremei C++

用预处理器替换 C++ 类/静态方法?

C++ staticconst和static const 以及它们的初始化

self与cls的区别:python中类方法的定义