类模板静态成员初始化两次

时间:2018-12-01 08:43:55

标签: c++ class templates static initialization

我的 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 两次。因此,我想知道为什么这两个静态变量之间的行为有所不同。

这是该程序的文件,用于演示错误。

Instance.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(完整版- 191426433 )进行编译。此外,调试和发布模式没有什么区别。

1 个答案:

答案 0 :(得分:1)

此代码对我来说似乎是正确的:count_defaultcount_static_assign具有常量表达式作为初始值设定项,因此必须在进行任何动态初始化之前对其进行初始化。 STATIC_TYPE是动态初始化。

OP报告说,将std::numeric_limits<Key>::min()更改为0可以修复程序的行为,因此我推测编译器存在一个错误,它不考虑constexpr函数{{1 }}为常量表达式。


要解决此问题,您可以尝试其他方法为std::numeric_limits<Key>::min()提出一个常量初始化程序,例如您自己编写的constexpr函数,或您使用的每种类型的专门化。