C++11 用户自定义数据标识

2017-04-14  本文已影响0人  深红的眼眸

本文根据众多互联网博客内容整理后形成,引用内容的版权归原始作者所有,仅限于学习研究使用,不得用于任何商业用途。

用户定义数据标识(User-defined literals)

C++提供了许多内建数据类型的数据标识(2.14节变量):

123    // int整型
1.2    // double双精度型
1.2F    // float浮点型
’a'    // char字符型
1ULL    // unsigned long long64位无符号长整型
0xD0    // hexadecimal unsigned十六进制无符号整型
"as"    // string字符串

但是在C++98中并没有为用户自定义的变量类型提供数据标识。这就违反甚至冲突于“用户自定类型应该和内建类型一样得到支持”的原则。在特殊情况下,人们有以下的需求:

"Hi!"s            //字符串,不是“以零字符为终结的字符数组”
1.2i            //虚数
123.4567891234df    //十进制浮点型(IBM)
101010111000101b    //二进制
123s            //秒
123.56km          //不是英里(单位)
1234567890123456789012345678901234567890x        //扩展精度

C++11通过在变量后面加上一个后缀来标定所需的类型以支持“用户定义数据标识”,自字义后缀用operator""定义,就是一种特殊的函数。后缀名必须以下划线开头,因为没有下划线的后缀是留给std用的。后缀的参数只能是unsigned long long、long double、const char或者const char + size_t。没了,它就是这么简单易上手又很实用的特性。一般来说适合编为后缀的是单位,如kg,km。例如:

constexpr complex operator "" i(long double d)    // 设计中的数据标识
{
    return {0,d};    //complex是一个数据标识
}

// 将n个字符构造成字符串std::string对象的数据标识
std::string operator""s (const char* p, size_t n)    
{
    return string(p,n);    // 需要释放存储空间
}

这里需要注意的是,constexpr的使用可以进行编译时期的计算。使用这一功能,我们可以这样写:

template <class T> void f(const T&);
f("Hello");    // 传递char*指针给f()
f("Hello"s);    // 传递(5个字符的)字符串对象给f()
f("Hello n"s);    // 传递(6个字符的)字符串对象给f()

auto z = 2+1i;    // 复数complex(2,1)

基本(实现)方法是编译器在解析什么语句代表一个变量之后,再分析一下后缀。用户自定义数据标识机制只是简简单单的允许用户制定一个新的后缀,并决定如何对它之前的数据进行处理。要想重新定义一个内建的数据标识的意义或者它的参数、语法是不可能的。一个数据标识操作符可以使用它(前面)的数据标识传递过来的处理过的值(如果是使用新的没有定义过的后缀的值)或者没有处理过的值(作为一个字符串)。

要得到一个没有处理过的字符串,只要使用一个单独的const char*参数即可,例如:

Bignum operator"" x(const char* p)
{
    return Bignum(p);
}

void f(Bignum);
f(1234567890123456789012345678901234567890x);

这个C语言风格的字符串”1234567890123456789012345678901234567890″被传递给了操作符 operator”” x()。注意,我们并没有明确地把数字转换成字符串。

有以下四种数据标识的情况,可以被用户定义后缀来使用用户自定义数据标识:

根据 C++ 11 标准,只有下面这些签名是合法的:

char const*
unsigned long long
long double
char const, std::size_t
wchar_t const*, std::size_t
char16_t const*, std::size_t
char32_t const*, std::size_t

上面列出的第一个签名不要同字符串相混淆,应该被称为原始字面量 raw literal 操作符。例如:

char const* operator"" _r(char const* s)
{
    return s;
}

int main()
{
    std::cout << 12_r << '\n';
}

注意,你为字符串标识定义的标识操作符不能只带有一个const char*参数(而没有大小)。例如:

//警告,这个标识操作符并不能像预想的那样子工作
string operator"" S(const char* p);  
"one two"S;    //错误,没有适用的标识操作符

根本原因是如果我们想有一个“不同的字符串”,我们同时也想知道字符的个数。后缀可能比较短(例如,s是字符串的后缀,i是虚数的后缀,m是米的后缀,x是扩展类型的后缀),所以不同的用法很容易产生冲突,我们可以使用namespace(命名空间)来避免这些名字冲突:

namespace Numerics {
    // …
    class Bignum { /* … */ };
    namespace literals {
        operator"" X(char const*);
    }
}
using namespace Numerics::literals;

参考资料

C++11 用户自定义字面值
C++11 新特性:用户定义字面量
【c++11FAQ】用户定义数据标识
cpp11新特性详解与应用

上一篇 下一篇

猜你喜欢

热点阅读