Search code examples
c++initializationconstexpr

Initialize static constexpr char array member with conditional operator in class template


Consider a minimal example

#include <iostream>

template<typename T>
struct foo
{
    // won't compile. how to change?
    static constexpr char sep[3] = std::is_integral<T>::value ? ". " : ", ";

    // many other things ...
};

int main()
{
    std::cout << foo<int>::sep << std::endl;     // prints .
    std::cout << foo<double>::sep << std::endl;  // prints ,
}

What I want to achieve is:

  • if T has an integral type, then sep is initialized to .
  • otherwise, sep is initialized to ,

However, the compiler won't allow this, saying

error: array must be initialized with a brace-enclosed initializer

It looks like something must be done in compile time. But I am not sure how to do it.

My question is: is there anything I can do to achieve this end?

Note: Minimal change is most welcome. There ought to be many other things in foo. Another consideration is that I want to keep everything about foo in header and to leave nothing in source file, if possible.

Thank you very much.


Solution

  • C-arrays are not copyable, so you have to work-around that

    • Do the check for each character:

      constexpr char sep[3] = { std::is_integral<T>::value ? '.' : ',', ' ', '\0' };
      
    • Don't use array but pointer (so you loose size):

      constexpr const char* sep = std::is_integral<T>::value ? ". " : ", ";
      
    • Use std::array:

      constexpr std::array<char, 3> sep = std::is_integral<T>::value
         ? std::array<char, 3>{{'.', ' ', 0}}
         : std::array<char, 3>{{',', ' ', 0}};
      
    • Use reference to array:

      constexpr char dot_sep[3] = std::is_integral<T>::value ? ". " : ", ";
      constexpr char comma_sep[3] = std::is_integral<T>::value ? ". " : ", ";
      constexpr const char (&sep)[3] = std::is_integral<T>::value ? dot_sep : comma_sep;
      

      and provide definition of dot_sep/comma_sep which are ODR-used.