Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Specialize static constexpr data member

I have a class to describe some traits of a type.

template<typename T>
struct my_traits
{
    static constexpr int some_trait = 0;

    static constexpr T min() { return std::numeric_limtis<T>::min(); }
    static constexpr T max() { return std::numeric_limits<T>::max(); }
};

I want to specialize my_traits::some_trait but when I try:

template<> constexpr int my_traits<int>::some_trait = 1;

The compiler complains that my_traits::some_trait already has an initializer. Of course I can specialize it by doing:

template<>
struct my_traits<int>
{
    static constexpr int some_trait = 1;

    // min and max
};

but then I have to redefine all the other functions, even though they will be exactly the same.

So how can I specialize my_traits<int>::some_trait without having to repeat min and max?

like image 602
maddisoj Avatar asked Mar 13 '16 17:03

maddisoj


People also ask

Is static needed for constexpr?

A non-static data member cannot be constexpr. static constexpr int x = 5; int y; };

What is constexpr static?

static defines the object's lifetime during execution; constexpr specifies that the object should be available during compilation. Compilation and execution are disjoint and discontiguous, both in time and space. So once the program is compiled, constexpr is no longer relevant.

What does constexpr mean?

constexpr indicates that the value, or return value, is constant and, where possible, is computed at compile time. A constexpr integral value can be used wherever a const integer is required, such as in template arguments and array declarations.

When to use #define vs constexpr?

#define directives create macro substitution, while constexpr variables are special type of variables. They literally have nothing in common beside the fact that before constexpr (or even const ) variables were available, macros were sometimes used when currently constexpr variable can be used.


1 Answers

There are several ways to do it. @Piotr Skotnicki and @Niall mentioned initializing through some helper that can be specialized. In general, just restructure your code so that you can specialize some classes or functions, and then use (by composition or inheritance) the specializing parts by parts which you don't need to specialize.

As an example of an alternative to the comments, here is a specializing base:

#include <iostream>                                                                                                                                                                                          
#include <limits>

template<typename T>
struct my_specializing_traits
{   
    static constexpr int some_trait = 0;
};  

template<>
struct my_specializing_traits<int>
{   
    static constexpr int some_trait = 1;
};  

Now you can just subclass it into a common part:

template<typename T>
struct my_traits :
    public my_specializing_traits<T>
{   
    static constexpr T min() { return std::numeric_limits<T>::min(); }
    static constexpr T max() { return std::numeric_limits<T>::max(); }
};  

The following shows it used (it outputs 0 and 1)

int main()
{   
    std::cout << my_traits<char>().some_trait << std::endl;
    std::cout << my_traits<int>().some_trait << std::endl;
}   
like image 73
Ami Tavory Avatar answered Sep 24 '22 08:09

Ami Tavory