【问题标题】:Class Template Static Member Initialized Twice类模板静态成员初始化两次
【发布时间】:2019-05-03 07:04:43
【问题描述】:

我的 Instance 类存在一个问题,我注意到两种初始化静态成员的方法导致的行为差异。

类模板 Instance 正在跟踪唯一计数。唯一计数用于跟踪特定类型的派生类的数量。它还用于为派生类分配唯一的 ID/索引。

第一个初始化如下:

template<typename Derived, typename Key>
Key Instance<Derived, Key>::count_static_assign{ std::numeric_limits<Key>::min() };

第二个是这样初始化的:

template<typename Derived, typename Key>
Key Instance<Derived, Key>::count_default{ 0 };

程序的输出

1 1
2 1

我希望这两个值相等,因为它们都应该递增。但是 count_static_assign 给出了错误的输出,因为它似乎重置并等于 1 两次。因此,我想知道为什么这两个静态变量的行为会有所不同。

这是演示错误的程序的文件。

实例.h

#ifndef INSTANCE_H
#define INSTANCE_H

#include <cinttypes>
#include <limits>
#include <iostream>

template<typename Derived, typename Key = std::uint16_t>
class Instance {
public:
    using KeyType = Key;
    static KeyType count_static_assign;
    static KeyType count_default;
public:
    Instance() = default;
    virtual ~Instance() = default;

    virtual KeyType getInstance() const = 0;
protected:
    static KeyType generate() {
        count_static_assign++;
        count_default++;
        std::cout << count_default << ' ' << count_static_assign << '\n';
        return count_default;
    }
};

//doesn't behave as expected
template<typename Derived, typename Key>
Key Instance<Derived, Key>::count_static_assign{ std::numeric_limits<Key>::min() };

//behaves as expected
template<typename Derived, typename Key>
Key Instance<Derived, Key>::count_default{ 0 };

#endif

Base.h

#ifndef BASE_H
#define BASE_H

#include <cinttypes>
#include <typeindex>
#include <memory>
#include "Instance.h"

class Base : public Instance<Base>
{
public:
    Base(){}
    ~Base(){}
};

template<typename Derived>
class CRTPBase : public Base {
public:
    static const KeyType STATIC_TYPE;

    CRTPBase() {}

    virtual ~CRTPBase() {}

    virtual KeyType getInstance() const override {
        return STATIC_TYPE;
    }
};

template<typename Derived>
const typename CRTPBase<Derived>::KeyType CRTPBase<Derived>::STATIC_TYPE = CRTPBase<Derived>::generate();

#endif

Foo.h

#ifndef FOO_H
#define FOO_H

#include "Base.h"

struct Foo : public CRTPBase<Foo> {
    Foo();
    ~Foo();
};

#endif

Foo.cpp

#include "Foo.h"

Foo::Foo()
{
}
Foo::~Foo()
{
}

Bar.h

#ifndef BAR_H
#define BAR_H

#include "Base.h"

struct Bar : public CRTPBase<Bar>
{
public:
    Bar();
    ~Bar();
};

#endif

Bar.cpp

#include "Bar.h"

Bar::Bar()
{
}
Bar::~Bar()
{
}

main.cpp

#include "Foo.h"
#include "Bar.h"
int main() {
    Foo foo;
    Bar bar;
    std::cin.get();
}

如果重要的话,我使用 Visual Studio 2017(Full Version-191426433) 编译。此外,调试和发布模式没有区别。

【问题讨论】:

  • 如果有关系,我正在使用 Visual Studio 编译 -- 你使用的是什么版本的编译器?
  • 添加了来自 _MSC_FULL_VER 的编译器版本号值
  • 不,只提Visual Studio 2010、2013、2015等
  • 明白了,把版本加在最后了。
  • 好的。你知道跨模块的静态初始化顺序没有定义吗?将所有这些代码粘贴到一个源文件中并进行测试。你得到不同的结果吗?

标签: c++ class templates static initialization


【解决方案1】:

这段代码在我看来是正确的:count_defaultcount_static_assign 将常量表达式作为初始化器,因此必须在任何动态初始化发生之前对其进行初始化。 STATIC_TYPE是动态初始化。

OP 报告说将std::numeric_limits&lt;Key&gt;::min() 更改为0 可以修复程序的行为,因此我推测编译器存在一个错误,即它不认为constexpr 函数std::numeric_limits&lt;Key&gt;::min() 是一个常量表达式。


要解决这个问题,您可以尝试一些其他方法来为count_static_assign 提供常量初始化器,例如您自己编写的 constexpr 函数,或者您使用的每种类型的特化。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-03-10
    • 1970-01-01
    • 2021-10-21
    • 2011-03-14
    • 1970-01-01
    • 2011-01-21
    相关资源
    最近更新 更多