【问题标题】:How to create static method that evaluates local static variable once?如何创建一次评估局部静态变量的静态方法?
【发布时间】:2011-01-29 18:52:27
【问题描述】:

我有一个带有静态方法的类,它有一个局部静态变量。我希望该变量被计算/评估一次(我第一次调用该函数)并且对于任何后续调用,它不再被评估。怎么做?这是我的课:

template<
    typename T1 = int, unsigned N1 = 1,
    typename T2 = int, unsigned N2 = 0,
    typename T3 = int, unsigned N3 = 0,
    typename T4 = int, unsigned N4 = 0,
    typename T5 = int, unsigned N5 = 0,
    typename T6 = int, unsigned N6 = 0,
    typename T7 = int, unsigned N7 = 0,
    typename T8 = int, unsigned N8 = 0,
    typename T9 = int, unsigned N9 = 0,
    typename T10 = int, unsigned N10 = 0,
    typename T11 = int, unsigned N11 = 0,
    typename T12 = int, unsigned N12 = 0,
    typename T13 = int, unsigned N13 = 0,
    typename T14 = int, unsigned N14 = 0,
    typename T15 = int, unsigned N15 = 0,
    typename T16 = int, unsigned N16 = 0>
struct GroupAlloc
{
    static const uint32_t sizeClass;
    static uint32_t getSize()
    {
        static uint32_t totalSize = 0;

        totalSize += sizeof(T1)*N1;
        totalSize += sizeof(T2)*N2;
        totalSize += sizeof(T3)*N3;
        totalSize += sizeof(T4)*N4;

        totalSize += sizeof(T5)*N5;
        totalSize += sizeof(T6)*N6;
        totalSize += sizeof(T7)*N7;
        totalSize += sizeof(T8)*N8;

        totalSize += sizeof(T9)*N9;
        totalSize += sizeof(T10)*N10;
        totalSize += sizeof(T11)*N11;
        totalSize += sizeof(T12)*N12;

        totalSize += sizeof(T13)*N13;
        totalSize += sizeof(T14)*N14;
        totalSize += sizeof(T15)*N15;
        totalSize += sizeof(T16)*N16;

        totalSize = 8*((totalSize + 7)/8);

        return totalSize;
    }
};

编辑:

感谢大家的及时帮助。对所有人+1。我选择了 Tyler McHenry 的答案,因为它不需要任何比较,纯静态函数评估。我将需要此代码用于分配器,因此避免另一个“如果”应该更好。再次感谢!

编辑:

gf 的答案被证明是最好的答案,因为它在编译时处理分配,并使程序免于线程安全头痛和显式初始化。但是,我尊重以前的最佳答案。我将在此处给予信用,而不是更改刻度线。感谢大家的帮助!

【问题讨论】:

  • 计算中的所有内容都是编译时常量。为什么不使用长公式初始化 totalSize。它将在编译时进行评估。您也可以将其设为静态类而不是本地静态。
  • 这只是标题,并且类 static const 存在问题,所以我解决了这个问题,而不是因为我没有尝试过。感谢您的建议。

标签: c++ templates static-methods lazy-evaluation static-variables


【解决方案1】:

将计算移到辅助函数中:

static uint32_t totalSize = calculateTotalSize();

只有在 totalSize 被初始化时才会调用辅助函数。

有点晚了,但你为什么要在这里进行(可能)运行时计算?使用编译时常量,你甚至不会有任何线程问题:

template<
  typename T1, unsigned N1,
  typename T2, unsigned N2,
  /* ... */
>
struct totalSize {
    static const uint32_t sum = 
        sizeof(T1)*N1
      + sizeof(T2)*N2
      /* ... */
      ;
    static const uint32_t value =
        8*((sum + 7)/8);
};

uint32_t GroupAlloc::getSize() {
    return totalSize<T1,N1,T2,N2,/*...*/>::value;
}

【讨论】:

  • 感谢您的加入!启发我永远不会迟到:)
  • 请注意,我不完全确定为什么我使用不同的结构 - 只是将值定义为 GroupAlloc 的静态常量成员应该在这里更好。
  • 嗯,我使用了相同的结构,而且它工作起来毫不费力!谢谢!
【解决方案2】:

创建另一个进行计算的静态函数,并将其用于变量的初始化,例如

static uint32_t computeSize() 
{
  uint32_t init_totalSize;

  // Lots of code

  return init_totalSize;
}

static uint32_t getSize()
{
  static uint32_t totalSize = computeSize();
  return totalSize;
}

静态变量保证只被初始化一次(第一次使用包含它们的函数)。

编辑:但这不是线程安全的。 This page explains why in great detail.

为了使其线程安全,将totalSize 的初始化(对computeSize 的调用)包装在临界区是不够的,因为静态变量初始化是“编译器魔法”,它可以是变量在调用getSize 之前的任何时候都会在它被使用之前进行初始化,甚至在函数的第一条语句之前。您需要做的是防止多个线程同时调用getSize,这可以通过另一种间接级别来完成,例如

static uint32_t computeSize() 
{
  uint32_t init_totalSize;

  // Lots of code

  return init_totalSize;
}

static uint32_t real_getSize()
{
  static uint32_t totalSize = computeSize();
  return totalSize;
}

static uint32_t getSize()
{
  uint32_t totalSize;
  /* --- Enter Critical Section (acquire lock) -- */
  totalSize = real_getSize();
  /* --- Exit Critical Section (release lock) -- */
  return totalSize;
}

这可以防止两个线程同时进入包含静态变量的函数,并确保其初始化将发生在临界区。

【讨论】:

  • 请问这个线程安全吗?如果没有,我需要在 computeSize() 函数中包装,rite?
  • 它不是线程安全的,但这个修复并不像包装 computeSize 那样简单。将此作为对答案的编辑...
  • 您可以通过在初始化线程之前调用一次方法来轻松地使其成为线程安全的。此时您的应用程序是单线程的。由于变量在整个执行过程中保持不变(您可以考虑将内部静态变量标记为const),在第一次完成调用后会出现线程安全问题。
  • ...最后一句话应该是:'there will not be thread safety...'
  • 可以将其设为线程安全(并且更快)以使 totalSize 成为静态成员。然后在启动任何线程之前由 computeSize 函数在程序加载时对其进行初始化。另外,函数 static 中的隐式 if 不必进行评估。再说一次,computeSize 可以在编译时进行评估,如果它适合 32 位 int,则可以进入 enum
【解决方案3】:
static uint32_t totalSize = 0;    // initialisation performed once only
if ( totalSize == 0 ) {
        totalSize += sizeof(T1)*N1;
        totalSize += sizeof(T2)*N2;
        totalSize += sizeof(T3)*N3;
        totalSize += sizeof(T4)*N4;
        // etc
}

【讨论】:

    【解决方案4】:

    类似:

    static uint32_t getSize()
    {
        static uint32_t totalSize = 0;
        static bool computed = 0;
        if(computed)
          return totalSize;
        computed = 1;
        // ... go on with your computation
    

    会成功的。请注意,它不是线程安全的。

    【讨论】:

      猜你喜欢
      • 2012-05-25
      • 2011-01-05
      • 2011-11-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-08-02
      • 1970-01-01
      • 2018-07-04
      相关资源
      最近更新 更多