understanding user defined string literals addition for c++20 - c++

I found in user defined string literal the following:
For user-defined string literals, let str be the literal without ud-suffix:
a) If the overload set includes a string literal operator template with a non-type template parameter for which str is a well-formed template argument, then the user-defined literal expression is treated as a function call operator "" X<str>(),
That sounds a bit mysterious to me. Can some one give an example how this can be used?
The following did not work at all and I can't catch the point what the non type template parameter for MyType can be. It seems not a char* nor const char*:
template < ??? >
struct MyType
{
const char* c;
constexpr MyType( const char* in ): c{in}{}
};
template < MyType t > auto operator ""_y() { return t; }
int main()
{
"Check it"_y;
}

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.

Related

C++20 concept matching string literal

In C++20 it is possible to write a wrapper class1 which only accepts string literals.
struct string_literal
{
template<size_t N>
consteval string_literal(char const (&s)[N]) : p(s) {}
char const* p;
};
void takes_literal(string_literal lit) {
// use lit.p here
}
Is it also possible to write a concept that only matches string literals?
1 This was the original question, but per the comments and answers here: the premise is flawed: it seems that this construct does not, in fact, accept only string literals.
A char const* which points into a string literal is virtually no different from any other char const*. The fact that a literal starts its life as an array is irrelevant, as non-literal character arrays can also be created. To my knowledge, there is exactly one difference: pointers to string literals cannot be used as non-type template parameters.
That's not particularly helpful. Even if we were restricted to compile-time code execution, you can get non-literal char const*s which also cannot be used as NTTPs (by creating a std::string, which can now be done at compile-time. You can call constexpr functions on string::c_strs pointer, but you can't use the result as an NTTP).
The best you can do is to create a user-defined literal operator that returns your string_literal object. You can make it so that only this literal operator can construct such a type (besides copying, of course). But even then, they can call your operator"" directly with a non-literal, and there is nothing you can do about it.
Instead, you should re-evaluate why you need to know if it is a string literal specifically.
Whereas const char(&)[N] isn't necessary a string literal, you might create concept to match const char(&)[N]:
template <typename T>
struct is_str_literal_impl : std::false_type{};
template <std::size_t N>
struct is_str_literal_impl<const char[N]> : std::true_type{};
template <typename T>
concept concept_str_literal = is_str_literal_impl<T>::value;
void takes_literal(concept_str_literal auto& lit) {
// use lit.p here
}
Demo

std::source_location as non type template parameter

In my infinite quest to push limits of what can be used as non type template parameter I was trying to see if I can use std::source_location as non type template parameter.
That failed with a weird message, since I presume source_location is some magical struct...
type 'std::experimental::source_location' of non-type template
parameter is not a structural type
It failed, so I tried to workaround that with using .file_name, but that also fails (godbolt).
note: candidate template ignored: substitution failure: pointer to
subobject of string literal is not allowed in a template argument
#include<iostream>
#include<experimental/source_location>
template<auto src_loc = std::experimental::source_location::current().file_name()>
void log_first(){
static bool dummy =([]{
std::cout << "Logging first call" + src_loc << std::endl;
}(), false);
}
int main() {
log_first();
log_first();
}
Is there any way to make this work without use of macros?
To be clear I am asking about using source_location as template parameter, not about solving my toy example, it is just here to demonstrate potential use case.
std::source_location is specified as:
struct source_location {
// ...
private:
uint_least32_t line_; // exposition only
uint_least32_t column_; // exposition only
const char* file_name_; // exposition only
const char* function_name_; // exposition only
};
And the rules for the kinds of types that can be used as non-template template parameters require that a type be structural, which means, from [temp.param]/7, emphasis mine:
A structural type is one of the following:
a scalar type, or
an lvalue reference type, or
a literal class type with the following properties:
all base classes and non-static data members are public and non-mutable and
the types of all bases classes and non-static data members are structural types or (possibly multi-dimensional) array thereof.
source_location does not have all of its non-static data members public, so it is not structural, so it is not usable as a non-type template parameter.
This part:
template <auto src_loc = std::experimental::source_location::current().file_name()>
does not work because of [temp.arg.nontype]/3:
For a non-type template-parameter of reference or pointer type, or for each non-static data member of reference or pointer type in a non-type template-parameter of class type or subobject thereof, the reference or pointer value shall not refer to or be the address of (respectively):
[...],
a string literal object ([lex.string]),
...
But what you can do is create your own type, that is structural, that is constructible from source_location. It's just that the strings can't be char const*, they have to own the data. If you look in the examples in P0732, we can build up:
template <typename Char, size_t N>
struct basic_fixed_string { ... };
template <basic_fixed_string S> struct A {};
using T = A<"hello">;
That might be awkward to deal with in this case, so you could also just pick some reasonable max size and go with that.

Does sizeof operator cause templates argument deduction to occur?

I know that sizeof operator doesn't evaluate its expression argument to get the answer. But it is not one of the non-deducted contexts for templates. So I am wondering how it interacts with templates and specifically template argument deductions. For instance, the following is taken from C++ Templates: The Complete Guide:
template<typename T>
class IsClassT {
private:
typedef char One;
typedef struct { char a[2]; } Two;
template<typename C> static One test(int C::*);
template<typename C> static Two test(...);
public:
enum { Yes = sizeof(IsClassT<T>::test<T>(0)) == 1 };
enum { No = !Yes };
};
This type function determines, as its name suggests, whether a template argument is a class type. The mechanism is essentially the following condition test:
sizeof(IsClassT<T>::test<T>(0)) == 1
Note, however, the function template argument is explicit (T in this case) and the function argument is a plan 0 of type int, which is not of type pointer to an int member of class C. In normal function template argument deduction, when T is really of class type and function argument is simply a 0, deduction on static One test(int C::*); should fail since implicit conversion (0 used as null pointer type) is not allowed during template argument deduction and (I guess?) SFINAE should kick in and overload resolution would have selected
static Two test(...);
However, since the whole expression is wrapped inside the sizeof operator, it seems that passing the 0 without a cast works.
Can someone clarify:
if my understanding of function template argument deduction is correct?
if it is because of the non-evaluation nature of sizeof operator that makes passing 0 successful? And
if 0 doesn't matter in this context, we could choose any argument in place of 0, such as 0.0, 100 or even user defined types?
Conclusion: I found in C++ Primer that has a section on function template explicit arguments. And I quote "Normal Conversions Apply for Explicitly Specified Arguments" and "For the same reasons that normal conversions are permitted for parameters that
are defined using ordinary types (§ 16.2.1, p. 680), normal conversions also apply
for arguments whose template type parameter is explicitly specified". So the 0 in this question is actually implicitly converted to null pointer to members (pointer conversion).
Template Argument Deduction is done when instantiating a function. This is done as part of function overloading (and other contexts not applicable here). In TAD, the types of function arguments are used to deduce the template parameters, but not all arguments are necessarily used. This is where the "non-deduced context" comes from. If a template parameter appears in a non-deduced context within a function signature, it can't be deduced from the actual argument.
sizeof(T) is in fact a non-deduced context for T, but it's so obvious that nobody even bothered to mention it. E.g.
template< int N> class A {};
template<typename T> void f(A<sizeof(T)>);
f(A<4>());
The compiler isn't going to pick a random T that has sizeof(T)==4.
Now your example actually doesn't have a sizeof inside the argument list of a function template, so "non-deduced context" is an irrelevant consideration. That said, it's important to understand what "sizeof doesn't evaluate its expression argument" means. It means the expression value isn't calculated, but the expression type is. In your example, IsClassT<T>::test<T>(0) won't be called at runtime, but its type is determined at compile time.

String literal with dependent type — impossible?

Is it possible to define a user-defined string literal conversion operator such that the type of its result depends on the value of its string input?
It is easy with user-defined integer and floating point literals because they admit literal operator templates, and the actual characters of the literal are passed to it as template arguments. Example:
template <char... s> struct silly { using type = int; };
template <char... s> struct silly<'1', s...> { using type = double; };
template <char... s>
typename silly<s...>::type operator"" _silly() { return 0; }
static_assert(std::is_same<int, decltype(4321_silly)>::value, "no luck");
static_assert(std::is_same<double, decltype(1234_silly)>::value, "no luck");
No such thing seems to exist for user-defined string literals.
Is there perhaps another way to do this, either in the current standard or planned/discussed for some future revision?
No, not possible, outside of serious macro hackery. String literals are accessed via constexpr, and return type of constexpr cannot depend on values of arguments.
The proposed <char...> operator"" for string literals ran into issues with "raw or processed" issues and how to specify it, and where dropped because hashing out those issues in time for the next standard would be hard, and/or ROI would be low. (at least from my casual reading of what happened).
I do not know if it died on the vine, or is still being worked on.
The hackery would be to pass <arr[0], arr[1], arr[2]> to a template pseduo-manually, and would not (directly) involve user defined literal syntax. It has many issues.

template and what is created during compilation

if i have a template function:
template<class T, class S>
void foo(T t, S s){..//do something//}
and then, inside the main i do this:
string str = "something";
char* ch = "somthingelse";
double num = 1.5;
foo(ch, num);
foo(num, ch);
foo(str, num);
..
my question is in the compilation what code will be written at the executable?
is it will be:
foo<char*, double>(..);
foo<double, char*>(..);
foo<string, double>(..);
or the compile will know at the second call to foo to change the place of the classes.
or in the 3rd one, in implicit way to use char* to create a string class?
Usually it will instantiate all three. They will not seek default-cast-workarounds to save binary image space.
il will not implicitly use
foo<string, double>(...)
for
foo(str, num)
but you can explicitly ask to use it, i.e. by calling
foo(string(str), num)
I think the following quote from the Standard clarifies this:
$14.9.1/6- "Implicit conversions
(Clause 4) will be performed on a
function argument to convert it to the
type of the corresponding function
parameter if the parameter type
contains no template-parameters that
participate in template argument
deduction. [ Note: template parameters
do not participate in template
argument deduction if they are
explicitly specified."
Since in this case the parameter types of the function template participate in function template argument deduction, no implicit conversion e.g. of string to char * take place.