Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

clang - how to declare a static const int in header file?

Given the following template in a header file, and a couple of specializations:

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

template<> const int A<int>::value = 1;
template<> const int A<long>::value = 2;

and building with clang-5, it results in errors for each source unit that included the file, all complaining about multiple definitions for A<int>::value and A<long>::value.

At first, I thought that maybe the template specializations needed to be put in a specific translation unit, but on checking the spec, this apparently should be allowed, because the value is a constant integer.

Am I doing something else wrong?

EDIT: if I move the definition into a single translation unit, then I can no longer use the value of A<T>::value in the context of a const int (eg, where its value is being used to calculate the value of another const assignment) , so the value really needs to be in a header.

like image 749
markt1964 Avatar asked Dec 06 '25 00:12

markt1964


1 Answers

In c++11 you maybe can go that way:

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;

If you only want to specialize the value var, you can use CRTP for that.

From C++17 you can make your definition inline:

template<> inline const int A<int>::value = 1;
template<> inline const int A<long>::value = 2;

Also from c++17 you can remove the 'template const int B::value;' for 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;

And another solution for c++11 can be to use a inline method instead of inline vars which are allowed from 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;

In addition to your last edit:

To use your values also in other dependent definitions it seems to be the most readable version if you use the inline constexpr methods.

Edit: "Special" version for clang, because as OP tells us, clang complains with "specialization happening after instantiation". I don't know if clang or gcc is wrong in that place...

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

I told already that CRTP is possible if not the complete class should be redefined. I checked the code on clang and it compiles without any warning or error, because OP comments that he did not understand how to use it:

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;
}
like image 174
Klaus Avatar answered Dec 07 '25 22:12

Klaus