What New Capabilities Do User-Defined Literals Add to C++

What new capabilities do user-defined literals add to C++?

Here's a case where there is an advantage to using user-defined literals instead of a constructor call:

#include <bitset>
#include <iostream>

template<char... Bits>
struct checkbits
{
static const bool valid = false;
};

template<char High, char... Bits>
struct checkbits<High, Bits...>
{
static const bool valid = (High == '0' || High == '1')
&& checkbits<Bits...>::valid;
};

template<char High>
struct checkbits<High>
{
static const bool valid = (High == '0' || High == '1');
};

template<char... Bits>
inline constexpr std::bitset<sizeof...(Bits)>
operator"" _bits() noexcept
{
static_assert(checkbits<Bits...>::valid, "invalid digit in binary string");
return std::bitset<sizeof...(Bits)>((char []){Bits..., '\0'});
}

int
main()
{
auto bits = 0101010101010101010101010101010101010101010101010101010101010101_bits;
std::cout << bits << std::endl;
std::cout << "size = " << bits.size() << std::endl;
std::cout << "count = " << bits.count() << std::endl;
std::cout << "value = " << bits.to_ullong() << std::endl;

// This triggers the static_assert at compile time.
auto badbits = 2101010101010101010101010101010101010101010101010101010101010101_bits;

// This throws at run time.
std::bitset<64> badbits2("2101010101010101010101010101010101010101010101010101010101010101_bits");
}

The advantage is that a run-time exception is converted to a compile-time error.
You couldn't add the static assert to the bitset ctor taking a string (at least not without string template arguments).

understanding user defined string literals addition for c++20

This is confusing wording, which was copied directly from the standard:

If [the overload set] contains a literal operator template with a non-type template parameter for which str is a well-formed template-argument

The confusing bit is the question of what "for which str is a well-formed template argument" specifically applies to. A direct reading of the passage from the standard suggests that "for which" refers to the "non-type template parameter", since that is the text directly preceding the words "for which". However, if you look at how the standard says the function will be invoked, you see this:

operator "" X<str>()

str is being passed to the operator, which the implication being that an implicit conversion will take place between str and the "non-type template parameter". That is, str is a valid "template argument" of the overloaded function, not of the template parameter of the overloaded function. And thus, the "for which" part should refer to the "literal operator template with a non-type template parameter", not the "non-type template parameter".

That having been said, to make your code work, you need to do more than to just remove the template argument from MyType.

You might have noticed a certain oddity in C++ surrounding non-type template parameters (NTTP). For example, NTTPs have always been able to be pointers to things. But you could never do this:

template<const char *literal> void foo() {}
foo<"literal">();

The standard expressly forbids a pointer NTTP from being initialized with a string literal. And C++20 does not change this.

Therefore, you can't take a pointer. You have to take what the literal actually is: an array. But you can't make your code work by taking const char (&in)[] as a parameter either. A literal is not an unsized array (since an "unsized array" is not a real object type). That array parameter must be sized appropriately to the literal.

Which means that you must deduce the size from a size template parameter.

Also, other rules flat-out forbid you from ever storing a pointer to a string literal in an NTTP (directly or indirectly). So, if you want a type that represents an entire string literal in an NTTP, that NTTP type must contain an array that is sized to that size.

So the simplest, functional string literal NTTP you could build would be:

template<size_t N>
struct string_literal
{
std::array<char, N> arr_;

constexpr string_literal(const char(&in)[N]) : arr_{}
{
std::copy(in, in + N, arr_.begin());
}
};

And thanks to CTAD, you can just use template < string_literal t > auto operator ""_y() to define your UDL.

Note that this string_literal class explicitly includes the NUL terminator as part of the array.

User-defined literals


unsigned long long operator "" _w(unsigned long long);
unsigned operator "" _u(const char*);

int main() {
12_w; // calls operator "" _w(12ULL)
12_u; // calls operator "" _u("12")
}

A little bit changes based on the example in your link.

Here 12_w calls operator "" _w(12ULL) since there is a literal operator with the parameter type unsigned long long, while 12_u calls operator "" _u("12") since there is only a raw literal operator.

Is there a workaround to define a user-defined literal for shorts in c++?

As explained here, the unary - is applied to the result of 42_s, so it seems that integral promotion cannot be avoided. Depending on applications, the following workaround might be of some use:

struct Short {    
short v;

short operator+() const {
return v;
}

short operator-() const {
return -v;
}
};

Short operator"" _s(unsigned long long x) {
return Short{static_cast<short>(x)};
}

auto PositiveShort = +42_s;
auto NegativeShort = -42_s;

static_assert(std::is_same_v<decltype(PositiveShort), short>);
static_assert(std::is_same_v<decltype(NegativeShort), short>);

Overloading rules for User-defined-literals in c++0x

I believe that 13.5.8/7 clarifies this issue :

Note: literal operators and literal
operator templates are usually invoked
implicitly through user-defined
literals (2.14.8). However, except for
the constraints described above
, they
are ordinary namespace-scope functions
and function templates. In particular,
they are looked up like ordinary
functions and function templates and
they follow the same overload
resolution rules.

From my understanding, regular overload resolution rules are only implied for literal operators when called outside an implicit invocation through user-defined literals.

So I think that if 2 & 3 are defined, 13_xx calls 2 (the raw literal operator).

User defined literals definitions

template <char...> double operator "" _π(); // OK

How does this operator work? How can you call it?

1.234_π will call operator "" _π<'1', '.', '2', '3', '4'>(). This form allows you to detect differences in spelling that would ordinarily be undetectable (1.2 vs 1.20, for example), and allows you to avoid rounding issues due to 1.2 not being exactly representable in even long double.

double operator"" _Z(long double); // error: all names that begin with underscore
// followed by uppercase letter are reserved
double operator""_Z(long double); // OK: even though _Z is reserved ""_Z is allowed

What is the difference between the above two functions?

The C++ standard defines the grammar in terms of tokens, which you can sort of interpret as words. "" _Z is two tokens, "" and _Z. ""_Z is a single token.

This distinction matters: given #define S " world!", and then "Hello" S, the whitespace is what makes S a standalone token, preventing it from being seen as a user-defined literal suffix.

For easier coding, both "" _Z and ""_Z syntaxes are generally allowed when defining these functions, but the "" _Z syntax requires _Z to be seen as an identifier. This can cause problems when an implementation predefines _Z as a macro, or declares it as a custom keyword.

User-defined literals, underscores, and global names

What's in a name? 3 Basic concepts [basic] tells us:

4 A name is a use of an identifier (2.11), operator-function-id (13.5), literal-operator-id (13.5.8), conversion-function-id (12.3.2), or template-id (14.2) that denotes an entity or label (6.6.4, 6.1).

which we cross-reference with 13.5.8 User-defined literals [over.literal]:

literal-operator-id:

operator "" identifier

While the name of a literal operator involves an identifier, that identifier does not denote an entity. (Or it's a different identifier and different name that denotes another entity or label altogether.) As such the name of a literal operator never starts with an underscore.

Something like operator""__w is problematic but this is not new: int i__0; is reserved as well.

C++0x, user-defined literals with friend operator ()

The Standard addresses this directly in the only place it mentions any restrictions on the declarations of user-defined literals, §13.5.8/2:

A declaration whose declarator-id is a literal-operator-id shall be a declaration of a namespace-scope function or function template (it could be a friend function (11.3)), an explicit instantiation or specialization of a function template, or a using-declaration (7.3.3).

If the friend is also declared at namespace scope, then there is no distinction between definition in class or namespace scope. Note that there is no requirement for definition at namespace scope, which your question as currently worded asserts.

If not declared at namespace scope, since it cannot be found by ADL, the friend could be used privately inside the class where it is scope by regular unqualified name lookup. This is the only way of declaring a literal operator which is not an external interface.

If the friend is defined inside a class template, then two instantiations of the template will generate two identically-named functions at namespace scope, which collide even though they are both invisible outside the class scope.



Related Topics



Leave a reply



Submit