【问题标题】:Advantage of replacing static const members with static member functions [duplicate]用静态成员函数替换静态常量成员的优势[重复]
【发布时间】:2017-01-16 17:03:00
【问题描述】:

我从一位我非常尊重的程序员那里发现了这样的代码:

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 为例,但我发现其他类型的问题也很有趣)。但是,我对这个问题的答案并不满意。在阅读了这里的一些答案后,我相信使用函数比易于模板化的能力更多。

【问题讨论】:

  • 好吧,因为它不是静态成员变量,所以不需要在类之外定义。
  • @NathanOliver IIRC 静态常量在使用 ODR 之前不需要定义。
  • @Quentin Typo 已修复。他们可能会在这种情况下使用它。
  • “但是,我对这个问题的答案并不满意。” 那就要求澄清或向受骗者提出赏金。在这里获得令人满意的答案并将这个问题的早期变体留下“差”的答案是没有意义的。 (是的,我知道你还没有代表,你需要赚取一些。)

标签: c++ c++11 static constants


【解决方案1】:

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

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

【讨论】:

  • 对未定义函数的调用可以像使用声明的静态全局函数一样被编译。两者都会导致链接器错误。
【解决方案2】:

一个原因是,在这种形式中,不需要在类之外定义静态成员,如果类在标头中实现,则可能不希望这样做。

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

【讨论】:

  • 好吧,既然这是一个整数类型的常量,那么无论如何都不需要在类之外定义它。显然,它不应该用作左值,只要它不是左值,就不需要定义。
  • 我认为这仍然是一种很好的做法
  • 引用相反的话,如果静态常量只打算用作右值,那么 not 定义它是一个好习惯 - 它可以防止滥用它作为左值。使定义成为可选项是 C++ 规范的重要且期待已久的补充。
  • 我的意思是把它放在一个函数中的好习惯。
【解决方案3】:

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

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

【讨论】:

  • 你应该改变忘记做在这必须忘记线程安全和静态初始化顺序惨败
  • 是的,确实,手机键盘在猜测我要输入的内容方面并没有变得更好......
  • 好的。我只是想检查一下你是否将不同的句子混合在一起。
  • 不太可能。这个问题似乎针对特定情况,其中常量值旨在成为编译时常量(积分常量表达式)。线程睿智和初始化顺序不是问题。
  • 您将静态初始化与动态混淆。这里不涉及动态初始化。
【解决方案4】:

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

例如:

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);

【讨论】:

  • 你可以拿指针。只需定义A::a
  • @NO_NAME 定义 A::a 可能是不可取的。
【解决方案5】:

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

【讨论】:

  • 在这里提出了类似的问题:stackoverflow.com/questions/16287776/…
  • 声明是针对动态计算为零的 constexpr 函数。我宁愿在任何其他声明上看到 constexpr 任何东西。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-31
  • 1970-01-01
相关资源
最近更新 更多