I was taking a look at the proposal N2765 (user-defined literals) already implemented on the development snapshots of the GCC 4.7 and I was thinking in how user-defined literals can be used to create some interesting and sometimes strange constructions.

Introduction to user-defined literals

C++03 has some literals, like the “f” in “12.2f” that converts the double value to float. The problem is that these literals aren’t very flexible since they’re pretty fixed, so you can’t change them or create new ones. To overcome this situation, C++11 introduced the concept of “user-defined literals” that will give to the user, the ability to create new custom literal modifiers. The new user-defined literals can create either built-in types (e.g. int) or user-define types (e.g. classes), and the fact that they could be very useful is an effect that they can return objects instead of only primitives.

The new syntax for the user-defined literals is:

1
OutputType operator "" _suffix(const char *literal_string);

… in the case of a literal string. The OutputType is anything you want (object or primitive), the “_suffix” is the name of the literal modifier, isn’t required to use the underline in front of it, but if you don’t use you’ll get some warnings telling you that suffixes not preceded by the underline are reserved for future standardization.

Examples

Kmh to Mph converter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// stupid converter class
class Converter
{
public:
    Converter(double kmph) : m_kmph(kmph) {};
    ~Converter() {};

double to_mph(void)
{ return m_kmph / 1.609344; }

private:
    double m_kmph;
};

// user-defined literal
Converter operator "" kmph(long double kmph)
{ return Converter(kmph); }

int main(void)
{
    std::cout << "Converter: " << (80kmph).to_mph() << std::endl;
    // note that I'm using parenthesis in order to
    // be able to call the 'to_mph' method
    return 0;
}

Note that the literal for for numeric types should be either long double (for floating point literals) or unsigned long long (for integral literals). There is no signed type, because a signed literal is parsed as an expression with a sign as unary prefix and the unsigned number part.

std::string literal

1
2
3
4
5
6
7
8
9
10
std::string operator "" s (const char* p, size_t n)
{ return std::string(p,n); }

int main(void)
{
    std::cout << "convert me to a string"s.length() << std::endl;
    // here you don't need the parenthesis, note that the
    // c-string was automagically converted to std::string
    return 0;
}

system() call

1
2
3
4
5
6
7
8
int operator "" ex(const char *cmd, size_t num_chars)
{ return system(cmd); }

int main(void)
{
    "ls -lah"ex;
    return 0;
}

alias and std::map

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
typedef std::map<std::string, int> MyMap;
MyMap create_map()
{
    MyMap m;
    m["lol"] = 7;
    return m;
}
auto m = create_map();

int& operator "" m(const char *key, size_t length)
{ return m[key]; }

int main(void)
{
    std::cout << "lol"m << std::endl;
    // 7
    "lol"m = 2;
    std::cout << "lol"m << std::endl;
    // 2
    return 0;
}

References

Wikipedia :: C++11 (User-defined literals)

Proposal N2765

0saves
If you enjoyed this post, please consider leaving a comment or subscribing to the RSS feed to have future articles delivered to your feed reader.