在 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 ,
}
我要实现的是:
- 如果
T
是整数类型,则sep
初始化为.
- 否则,
sep
初始化为,
然而,编译器不允许这样做,说
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::array
或 const char *
一起使用。如果有很多这样的功能,人们可能不想将它们全部升级到 std::array
。例如,此函数在以下情况下完美运行:
static constexpr unsigned nuls = count_nuls(foo<double>::sep);
但无法与 std::array<char, 3>
一起使用(未经进一步修改)。
考虑一个最小的例子
#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
里应该还有很多其他的东西。另一个考虑是,如果可能的话,我想在 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::array
或 const char *
一起使用。如果有很多这样的功能,人们可能不想将它们全部升级到 std::array
。例如,此函数在以下情况下完美运行:
static constexpr unsigned nuls = count_nuls(foo<double>::sep);
但无法与 std::array<char, 3>
一起使用(未经进一步修改)。