如何使用C ++ 11用户定义的文字获取最小的变量

时间:2013-06-21 04:37:16

标签: c++ templates c++11 variadic-templates user-defined-literals

我一直在研究C ++ 11的一些新功能,并对其中的一些特别印象深刻,特别是用户定义的文字。

这些允许您定义999_something形式的文字,其中something控制对999执行的操作以生成文字。所以不必再使用:

#define MEG * 1024 * 1024
int ten_meg = 10 M;

我认为这会很好地实现大量的下划线,比如1_000_000_blah,这与Perl的可读性相匹配,尽管Perl在某种程度上可读的想法对我来说似乎很幽默: - )

对于1101_1110_b0011_0011_1100_1111_b等二进制值也很方便。

显然,由于_个字符,这些字符需要是原始模式类型,处理C字符串,我也可以。

无法弄清楚的是如何根据操作数大小提供不同的类型。例如:

1101_1110_b

应该提供char(假设char当然是8位),同时:

0011_0011_1100_1111_b

将提供16位类型。

我可以从文字运算符函数operator""本身获取操作数的长度(通过计数数字字符),但返回类型似乎是固定到函数,所以我不能返回不同的类型对此。

可以在用户定义的类型框架中使用单个后缀_b完成此操作,还是需要手动拆分类型(_b8_b16等等)并提供大多数重复的功能?

2 个答案:

答案 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::vectorboost::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);
}