C++11 如何在头文件中声明静态常量int?

C++11 如何在头文件中声明静态常量int?,c++11,static,clang,constants,one-definition-rule,C++11,Static,Clang,Constants,One Definition Rule,给定头文件中的以下模板和几个专门化: template<typename> class A { static const int value; }; template<> const int A<int>::value = 1; template<> const int A<long>::value = 2; 模板类别A{ 静态常量int值; }; 模板常量int A::value=1; 模板常量int A::val

给定头文件中的以下模板和几个专门化:

template<typename> class A {
        static const int value;
};

template<> const int A<int>::value = 1;
template<> const int A<long>::value = 2;
模板类别A{
静态常量int值;
};
模板常量int A::value=1;
模板常量int A::value=2;
使用clang-5构建时,它会导致包含该文件的每个源单元出现错误,所有这些都抱怨
A::value
A::value
的多个定义

起初,我认为可能需要将模板专门化放在特定的翻译单元中,但在检查规范时,这显然是允许的,因为该值是一个常量整数

我做错什么了吗


编辑:如果我将定义移动到单个翻译单元中,那么我就不能再在
常量int
的上下文中使用
a::value
的值(例如,它的值用于计算另一个常量赋值的值),因此该值确实需要在头中。

在c++11中,您可以这样做:

template<typename> class B {
    public:
        static const int value = 1;
};

template<> class B<long> {
    public:
        static const int value = 2;
};

template<typename T> const int B<T>::value;
模板类B{
公众:
静态常数int值=1;
};
B类模板{
公众:
静态常数int值=2;
};
模板常量intb::值;
如果只想专门化值var,可以使用CRTP

从C++17中,您可以将定义内联:

template<> inline const int A<int>::value = 1;
template<> inline const int A<long>::value = 2;
模板内联常量int A::value=1;
模板内联常量int A::value=2;
也可以从c++17中删除“template const int B::value;”对于constexpr:

template<typename> class C {
    public:
        static constexpr int value = 1;
};

template<> class C<long> {
    public:
        static constexpr int value = 2;
};

// no need anymore for: template<typename T> const int C<T>::value;
模板类C{
公众:
静态constexpr int值=1;
};
模板类别C{
公众:
静态constexpr int值=2;
};
//不再需要:template const int C::value;
c++11的另一个解决方案是使用内联方法,而不是c++17允许的内联变量:

template<typename T> class D { 
    public:
        static constexpr int GetVal() { return 0; }

        static const int value = GetVal();
};  

template <> inline constexpr int D<int>::GetVal() { return 1; }
template <> inline constexpr int D<long>::GetVal() { return 2; }

template< typename T>
const int D<T>::value;
模板类D{
公众:
静态constexpr int GetVal(){return 0;}
静态常量int value=GetVal();
};  
模板内联constexpr int D::GetVal(){return 1;}
模板内联constexpr int D::GetVal(){return 2;}
模板
常量int D::值;
除了上次编辑之外:

要在其他依赖定义中也使用您的值,如果您使用内联constexpr方法,它似乎是最可读的版本

编辑:clang的“特殊”版本,因为正如OP告诉我们的,clang抱怨“实例化后发生专门化”。我不知道在那个地方clang或gcc是不是错了

template<typename T> class D {
    public:
        static constexpr int GetVal();
        static const int value;
};


template <> inline constexpr int D<int>::GetVal() { return 1; }
template <> inline constexpr int D<long>::GetVal() { return 2; }

template <typename T> const int D<T>::value = D<T>::GetVal();

int main()
{
    std::cout << D<int>::value << std::endl;
    std::cout << D<long>::value << std::endl;
}
模板类D{
公众:
静态constexpr int GetVal();
静态常量int值;
};
模板内联constexpr int D::GetVal(){return 1;}
模板内联constexpr int D::GetVal(){return 2;}
模板常量int D::value=D::GetVal();
int main()
{

std::cout因为您定义了专门化的变量,所以仍然需要将它们放在单个转换单元中。链接器将能够将它们放在一起。如果您需要使用常量的值来计算另一个常量的值,则这不起作用(需要知道赋值点处所有常量的值,如果从标题中删除,可能不在同一个转换单元中)。我不知道您为什么认为必须在标题中对其进行初始化。您可以在
.cpp
文件中定义
模板常量A::value=1;模板常量A::value=A::value+1;
。我是否遗漏了什么?@cantord我是否认为OP的目标是使常量成为编译时常量,以便可以在上下文中使用可以使用编译时常量的地方。@Someprogrammerdude我明白了。不过这不是很明显。@markt1964:我已经告诉过你可以使用CRTP,但我现在也为你添加了一个示例。已经添加了一个与grang兼容的GetVar方法专门化版本。
template<typename> class E_Impl {
    public:
        static const int value = 1;
};

template<> class E_Impl<long> {
    public:
        static const int value = 2;
};

template<typename T> const int E_Impl<T>::value;

template < typename T>
class E : public E_Impl<T>
{
    // rest of class definition goes here and must not specialized
    // and the values can be used here!

    public:

        void Check()
        {
            std::cout << this->value << std::endl;
        }
};


int main()
{
    E<long>().Check();
    std::cout << E<long>::value << std::endl;
    E<int>().Check();
    std::cout << E<int>::value << std::endl;
}