使用类模板中的条件运算符初始化静态constexpr char数组成员

时间:2018-10-21 14:14:59

标签: c++ initialization constexpr

考虑一个最小的例子

#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 ,
}

我想要实现的是:

  • 如果T具有整数类型,则sep初始化为.
  • 否则,sep被初始化为,

但是,编译器不允许这样做

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

似乎必须在编译时完成一些工作。但是我不确定该怎么做。

我的问题是:我能做些什么来实现这一目标吗?

注意:最小的更改是最受欢迎的。 foo中应该还有许多其他内容。另一个考虑因素是,我想将有关foo的所有内容保留在标头中,并尽可能在源文件中保留任何内容。

非常感谢您。

2 个答案:

答案 0 :(得分:2)

C数组不可复制,因此您必须解决该问题

  • 检查每个字符:

    constexpr char sep[3] = { std::is_integral<T>::value ? '.' : ',', ' ', '\0' };
    
  • 不要使用数组,而是使用指针(这样可以减小大小):

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

    constexpr std::array<char, 3> sep = std::is_integral<T>::value
       ? std::array<char, 3>{{'.', ' ', 0}}
       : std::array<char, 3>{{',', ' ', 0}};
    
  • 使用对数组的引用:

    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;
    

    并提供ODR使用的dot_sep / comma_sep的定义。

答案 1 :(得分:2)

最好的方法是使用具有特殊化功能的基类,并将sep放在基类中:

template <bool IsIntegral>
struct foo_base;

template<>
struct foo_base<true>
{
    static constexpr char sep[3] = ". ";
};

template<>
struct foo_base<false>
{
    static constexpr char sep[4] = ",  ";
};


template<typename T>
struct foo : foo_base<std::is_integral_v<T>>
{
    // many other things ...
};

但是,如果您不希望其他人访问该库,则可以使用私有继承:

template<typename T>
struct foo : private foo_base<std::is_integral_v<T>>
{
    using foo_base<std::is_integral_v<T>>::sep;
    // many other things ...
};

编辑

与使用std::array<char, 3>相比,此解决方案的优势在于,该解决方案可与接受对char的C数组的引用的函数很好地配合使用。存储const char*std::array<char, 3>都没有此功能。

例如,如果您具有以下功能:

template <std::size_t I>
constexpr int count_nuls(const char (&x)[I])
{
    // Can't use std::count, since it is not constexpr
    unsigned count = 0;
    for (auto ch: x)
        if (ch == '\0')
            ++count;
    return count;
}

此功能不能与std::arrayconst char *一起使用。如果有很多类似的功能,则可能不希望将所有功能都升级到std::array。例如,此功能可在以下环境中完美运行:

static constexpr unsigned nuls = count_nuls(foo<double>::sep);

但是std::array<char, 3>无法使用(未经进一步修改)。