使用静态成员函数替换静态const成员的优点

时间:2017-01-16 17:03:00

标签: c++ c++11 static const

我从程序员那里得到了这样的代码,我非常尊重:

class BigClass {
  using MyId = uint32_t;
  static constexpr MyId INVALID_ID() { return  std::numeric_limits<MyId>::max();};
  class SmallClass {
    /* Constructor, etc. */
    MyId id = INVALID_ID(); /* Default value */
  };
};

将INVALID_ID()定义为函数而不是静态const变量有明显的优势吗?

static constexpr variable vs function问题与我的问题完全相同(我以uint32_t为例,但我发现其他类型的问题也很有趣)。但是,我对这个问题的答案不满意。在阅读了这里的一些答案后,我相信使用函数比使用简单模板的能力更有优势。

5 个答案:

答案 0 :(得分:2)

除了简单的编码用法之外,我只能看到一个真正的区别:只有在使用odr时才需要定义一个静态const变量(例如在一个函数中使用ref ...)。问题是,如果不遵守此规则,则会出现链接时错误,程序员可能会在问题真正发生后浪费时间进行搜索。

当你以这种方式使用函数,并且尝试将它作为函数调用传递给需要ref的函数时,编译时错误将立即出现在程序员的面上,并带有显式错误消息。所以我会说未来的维护者更友好...

答案 1 :(得分:1)

这与线程安全和静态初始化命令fiasco有关。如果有两个相互依赖的静态变量在单独的翻译单元中定义(一个需要另一个的值来构建自身),则无法保证它们的初始化顺序。

使用包装器函数和C ++ 11保证编译器将在其初始化周围放置必要的线程锁,函数本地静态将对该函数进行并发调用,等待在执行之前初始化变量继续进行。

答案 2 :(得分:1)

一个原因是,在这种形式中,不需要在类之外定义静态成员,如果在头部内部实现类,则可能是不合需要的。

第二个原因是可扩展性,您可能有一天希望您的无效ID在其初始化中执行其他constexpr逻辑,而不是调用一个constexpr函数,如max

答案 3 :(得分:1)

人们试图通过用函数替换类静态常量成员来解决的一个问题是链接器错误。

例如:

struct A {
    static constexpr int a = 1;
};

void foo(int const&);

foo(A::a); // Linker error: A::a.

正确的解决方案是不要指向A::a的指针或引用(如果提供A::a的外联定义是不合需要的):

void foo(int);

答案 4 :(得分:-1)

我没有看到任何优势。我宁愿看INVALID_ID声明为const(constexpr)而不是函数,因为函数调用让读者想知道正在进行什么样的计算。