⬆️ ⬇️

C ++ 11 User Literals

More than half a year has passed since the adoption of the C ++ 11 standard. The network can find a lot of materials dedicated to the new standard, but most of them relate to the most simple features, the sweetest. I'm talking about lambda functions, automatic type inference, new specifiers, smart pointers, etc. Yes, these are really interesting things and, one can safely say, they are among the most useful and frequently used. But the wedge did not converge on them, and the new C ++ 11 offers us not only them.



Below I want to talk about user literals - a very useful tool, though not for everyday purposes.





What is a literal?



A literal is some expression that creates an object. Literals appeared not only in C ++ 11, they were also in C ++ 03. For example, there are literals to create a character, string, real numbers, etc.

')

'x'; // character "some"; // c-style string 7.2f; // float 74u; // unsigned int 74l; // long 0xF8; // hexadecimal number 


These are all literals. With the concept of literals, I think we figured out. It's time to go back to C ++ 11.



C ++ 11 User Literals



As noted above, the new standard offers the means to create custom literals. There are two categories of user literals: raw literals (raw) and literals for built-in types (cooked) .



It is worth noting, however, that C ++ allows you to create only literal suffixes. In other words, it is impossible to create literal prefixes (like, for example, 0x ), or prefix-suffix (like "" ).



Literals for numerical types


Let's start with literals for built-in types. To create a literal for numerical types, you must use one of two signatures:



  //      OutputType operator "" _suffix(unsigned long long); //      OutputType operator "" _suffix(long double); 


The use of the literal will be as follows:



  42_suffix; // OutputType operator "" _suffix(unsigned long long); 42.24_suffix; // OutputType operator "" _suffix(long double); 




Pay attention to the signatures:



These types are taken for a reason and cannot be replaced by others. They are mandatory and approved by the language standard.



Below is an example of a literal that converts minutes to seconds.



  unsigned long long operator "" _min(unsigned long long minutes) { return minutes * 60; } // ... std::cout << 5_min << std::endl; //    300 




Literals for string types


To create a literal of this type, you must use one of the following signatures:



  OutputType operator "" _suffix(const char* str, size_t size); OutputType operator "" _suffix(const wchar_t* str, size_t size); OutputType operator "" _suffix(const char16_t* str, size_t size); OutputType operator "" _suffix(const char32_t* str, size_t size); 


The signature is selected depending on the type of string:



  "1234"_suffix; // operator "" _suffix(const char* str, size_t size); u8"1234"_suffix; // operator "" _suffix(const char* str, size_t size); L"1234"_suffix; // operator "" _suffix(const wchar_t* str, size_t size); u"1234"_suffix; // operator "" _suffix(const char16_t* str, size_t size); U"1234"_suffix; // operator "" _suffix(const char32_t* str, size_t size); 


An example of a C-style converting literal string to std::string shown below.



  std::string operator "" s(const char* str, size_t size) { return std::string(str, size); } // ... std::cout << "some string"s.length() << std::endl; 




Raw Literals


Well, finally it is time raw crude. The signature of the raw literal is as follows:



  OutputType operator "" _suffix(const char* literalString); 


This type of literal comes to the rescue when the input number must be parsed character by character. T.e. in this case, the number is passed to the operator as a string. If not completely understood, take a look at the code below:



  OutputType operator "" _x(unsigned long long); OutputType operator "" _y(const char*); 1234_x; // call: operator "" _x(1234); 1234_y; // call: operator "" _y("1234"); 


Using this type of literal, you can write a literal that converts a binary number to a decimal. For example, like this:



  unsigned long long operator "" _b(const char* str) { unsigned long long result = 0; size_t size = strlen(str); for (size_t i = 0; i < size; ++i) { assert(str[i] == '1' || str[i] == '0'); result |= (str[i] - '0') << (size - i - 1); } return result; } // ... std::cout << 101100_b << std::endl; //  44 


There is another signature for raw literals. It is based on the use of the Variadic Template :



  template <char...> OutputType operator "" _b(); 


The advantages of the Variadic Template literals are that they can be computed at compile time. The same binary to decimal literal can be rewritten as:



  template <char... bits> struct to_binary; template <char high_bit, char... bits> struct to_binary<high_bit, bits...> { static_assert(high_bit == '0' || high_bit == '1', "Not a binary value!"); static const unsigned long long value = (high_bit - '0') << (sizeof...(bits)) | to_binary<bits...>::value; }; template <char high_bit> struct to_binary<high_bit> { static_assert(high_bit == '0' || high_bit == '1', "Not a binary value!"); static const unsigned long long value = (high_bit - '0'); }; template <char... bits> constexpr unsigned long long operator "" _b() { return to_binary<bits...>::value; } // ... int arr[1010_b]; //      std::cout << 101100_b << std::endl; //  44 


An attentive reader might ask: “What if you create both a raw literal and a literal for a number with the same name? What literal will the compiler use? ” The standard gives an exact answer on this and speaks about the compiler’s attempt to use literals in the following order:





It is useful to know that if a user-defined literal coincides with the system (for example, f ), then the system literal will be executed.



  long operator "" f(long double value) { return long(value); } // ... std::cout << 42.7f << std::endl; //  42.7 




findings



Björn Straustrup at Going Native 2012 gave a useful example of using literals. It seems to me that it vividly demonstrates the fact of increasing the readability of the code, and also reduces the chance of a mistake.



  Speed sp1 = 100m / 9.8s; // very fast for a human Speed sp2 = 100m / 9.8s2; // error (m/s2 is acceleration) Speed sp3 = 100 / 9.8s; // error (speed is m/s and 100 has no unit) 




The custom literal mechanism is a useful tool in some cases. To use it anywhere is not worth it. Think twice before using them, because literals are cunning: they can ...







ps:

Custom literals are supported by the gcc 4.7 and clang 3.1 compilers .

Source: https://habr.com/ru/post/140357/



All Articles