我创建了一个用户定义的文本_c,以将“整数”文本转换为std::integral_constant。基本上,目标是允许用户编写std::integral_constant实例而不使用通常的样板。以下是实现:
#include <type_traits>
template<typename T, typename U>
constexpr auto pow_helper(T acc, T value, U times)
-> T
{
return (times > 1) ?
pow_helper(acc*value, value, times-1) :
acc;
}
// Compile-time pow function, only works with
// an unsigned integer exponent
template<typename T, typename U>
constexpr auto pow(T value, U exponent)
-> T
{
return (exponent == 0) ? 1 :
(exponent > 0) ? pow_helper(value, value, exponent) :
1 / pow_helper(value, value, -exponent);
}
// Structure to parse an integer literal
template<typename Integral, char C, char... Digits>
struct parse
{
static constexpr Integral value =
parse<Integral, C>::value * pow(10u, sizeof...(Digits))
+ parse<Integral, Digits...>::value;
};
// Specialization of parse to parse a single
// decimal digit
template<typename Integral, char C>
struct parse<Integral, C>
{
static_assert(C >= '0' && C <= '9',
"only characters in range 0..9 are accepted");
static constexpr Integral value = C - '0';
};
// User defined literal for std::integral_constant
template<char... Digits>
constexpr auto operator"" _c()
-> std::integral_constant<int, parse<int, Digits...>::value>
{
return {};
}使用这个文本,wirting 42_c生成一个std::integral_constant<int, 42>实例。下面是一个小的工作示例:
int main()
{
std::cout << 45_c << '\n'; // prints 45
std::cout << -23_c << '\n'; // prints -23
static_assert(std::is_same<decltype(58_c), std::integral_constant<int, 58>>::value, "");
}为了生成其他积分常量,我计划添加用户定义的文本_cl、_cll、_cu、_cul和_cull whoe实现完全相同,只有结果类型不同。
是否有办法改进这段代码并/或使其更简洁或更地道?我错过了一些潜在的缺陷吗?
发布于 2014-05-23 19:43:21
我会尽量避免在有别名模板或constexpr函数的优雅的替代解决方案时使用成熟的类模板。请注意,我没有度量这一点,因此,可以使用少量的盐分:轻量级的constexpr函数和别名模板可以更快地实例化。使用constexpr函数可以将实例化的数量减少到至少1(文字操作符模板)或两个(具有单个模板类型参数的constexpr函数模板)。
通过更改操作顺序,您可以完全摆脱pow函数(模板):将当前结果传递到下一步,然后乘并添加。换挡(在基地10)将在飞行中进行。
素描:
constexpr int combine(int p)
{
return p;
}
template<class... TT>
constexpr int combine(int val, int p0, TT... pp)
{
return combine(val*10 + p0, pp...);
}
constexpr int parse(char C)
{
return (C >= '0' && C <= '9')
? C - '0'
: throw std::out_of_range("only decimal digits are allowed");
}
template<char... Digits>
constexpr auto operator"" _c()
-> std::integral_constant<int, combine(0, parse(Digits)...)>
{
return {};
}您的解析函数目前拒绝十六进制、八进制和C++1y二进制文本,以及C++1Y的数字分隔符。支持基数< 10是相当简单的,因为你只需要改变“移动”因素。即乘以8或2,而不是10。对于基数16,还需要在字符->数字转换中添加一些代码。IIRC,不能保证这些字母在基本执行字符集(相对于数字)中是连续的,因此这可能是相当痛苦的切换或查找表。此外,上/下大小写。
正常的C++文本会根据其值自动调整其类型。如果他们不适合一个int,他们会尝试long,long long等。也许其中一个用户定义的文字应该模仿这种行为,以方便。
不幸的是,StdLib没有为std::integral_constant提供运算符。因此,-23_c将不是std::integral_constant<int, -23>,而是int (通过隐式转换运算符)。我觉得很奇怪。
考虑使用自定义类型,可能从/可转换到std::integral_constant,并为这种类型提供(元编程)运算符。
旁注:
lit<45>更容易实现:
template<std::uintmax_t N>
using lit = std::integral_constant<std::uintmax_t, N>;https://codereview.stackexchange.com/questions/50910
复制相似问题