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

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

考虑一个最小的例子

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

我要实现的是:

然而,编译器不允许这样做,说

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

看起来有些事情必须在编译时完成。但是我不知道该怎么做。

我的问题是:我能做些什么来达到这个目的吗?

注意:欢迎进行最小的更改。 foo里应该还有很多其他的东西。另一个考虑是,如果可能的话,我想在 header 中保留关于 foo 的所有内容,并且不在源文件中留下任何内容。

非常感谢。

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

  • 检查每个字符:

    constexpr char sep[3] = { std::is_integral<T>::value ? '.' : ',', ' ', '[=10=]' };
    
  • 不要使用数组,而是使用指针(这样你就放宽了大小):

    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 的定义。

最好的方法是使用特化的基class,并将sep放在基class:

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

但是如果不想让其他人访问base,可以使用私有继承:

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> 相比,此解决方案的优势在于,此解决方案可以很好地处理接受对 C char 数组的引用的函数。存储 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 == '[=12=]')
            ++count;
    return count;
}

此函数不能与 std::arrayconst char * 一起使用。如果有很多这样的功能,人们可能不想将它们全部升级到 std::array。例如,此函数在以下情况下完美运行:

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

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