我一直在研究C ++ 11的一些新功能,并对其中的一些特别印象深刻,特别是用户定义的文字。
这些允许您定义999_something
形式的文字,其中something
控制对999
执行的操作以生成文字。所以不必再使用:
#define MEG * 1024 * 1024
int ten_meg = 10 M;
我认为这会很好地实现大量的下划线,比如1_000_000_blah
,这与Perl的可读性相匹配,尽管Perl在某种程度上可读的想法对我来说似乎很幽默: - )
对于1101_1110_b
和0011_0011_1100_1111_b
等二进制值也很方便。
显然,由于_
个字符,这些字符需要是原始模式类型,处理C字符串,我也可以。
我无法弄清楚的是如何根据操作数大小提供不同的类型。例如:
1101_1110_b
应该提供char
(假设char
当然是8位),同时:
0011_0011_1100_1111_b
将提供16位类型。
我可以从文字运算符函数operator""
本身获取操作数的长度(通过计数数字字符),但返回类型似乎是固定到函数,所以我不能返回不同的类型对此。
可以在用户定义的类型框架中使用单个后缀_b
完成此操作,还是需要手动拆分类型(_b8
,_b16
等等)并提供大多数重复的功能?
答案 0 :(得分:6)
你需要知道字符串的大小,唯一的方法就是让参数包使用sizeof...
。您应该能够使用可变参数模板operator""
实现您想要的目标:
#include <cstdint>
#include <type_traits>
template<char... String>
auto operator "" _b()
-> typename std::conditional<sizeof...(String) <= 8,
uint8_t,
typename std::conditional<sizeof...(String) <= 16,
uint16_t,
uint32_t
>::type
>::type
{
// Do whatever you want here
}
这是一个测试用例:
int main()
{
auto a = 10000001_b;
auto b = 100000001_b;
std::cout << std::boolalpha;
std::cout << std::is_same<decltype(a), uint8_t>::value << "\n"; // true
std::cout << std::is_same<decltype(b), uint16_t>::value << "\n"; // true
}
不幸的是,该解决方案无法处理数字分隔符。而且,std::conditional
机器非常难看。您可以使用boost::mpl::vector
,boost::mpl::at
和一些算术运算来更好地工作。
答案 1 :(得分:0)
由于Morwenn's answer,我为陷入C ++ 11的那些人提供了一个针对用户定义的二进制文字的完整解决方案:
#include <cstdint>
#include <type_traits>
/// User-defined binary literal for C++11
/// @see https://stackoverflow.com/a/538101 / https://gist.github.com/lichray/4153963
/// @see https://stackoverflow.com/a/17229703
namespace detail {
template<class tValueType, char... digits>
struct binary_literal;
template<char... digits>
struct unsigned_binary_literal
{
using Type = typename std::conditional<sizeof...(digits) <= 8, uint8_t,
typename std::conditional<sizeof...(digits) <= 16, uint16_t,
typename std::conditional<sizeof...(digits) <= 32, uint32_t, uint64_t>::type
>::type
>::type;
};
template<char... digits>
struct signed_binary_literal
{
using Type = typename std::conditional<sizeof...(digits) <= 8, int8_t,
typename std::conditional<sizeof...(digits) <= 16, int16_t,
typename std::conditional<sizeof...(digits) <= 32, int32_t, int64_t>::type
>::type
>::type;
};
template<class tValueType, char high, char... digits>
struct binary_literal<tValueType, high, digits...>
{
static constexpr tValueType value = (static_cast<tValueType>(high == '1') << (sizeof...(digits))) ^ binary_literal<tValueType, digits...>::value;
};
template<class tValueType, char high>
struct binary_literal<tValueType, high>
{
static constexpr tValueType value = (high == '1');
};
} // namespace detail
/// C++11 support for binary literal
/// @tparam digits to transform to an unsigned integer
template<char... digits>
constexpr auto operator "" _bin() -> typename detail::unsigned_binary_literal<digits...>::Type
{
return detail::binary_literal<typename detail::unsigned_binary_literal<digits...>::Type, digits...>::value;
}
/// C++11 support for binary literal
/// @tparam digits to transform to a signed integer
template<char... digits>
constexpr auto operator "" _sbin() -> typename detail::signed_binary_literal<digits...>::Type
{
return static_cast<typename detail::signed_binary_literal<digits...>::Type>(detail::binary_literal<typename detail::unsigned_binary_literal<digits...>::Type, digits...>::value);
}