How to set string as value template parameter? [duplicate] - c++

I understand that non-type template parameters should be a constant integral expression. Can someone shed light why this is so?
template <std::string temp>
void foo()
{
// ...
}
error C2993: 'std::string' : illegal type for non-type template parameter 'temp'.
I understand what a constant integral expression is. What are the reasons for not allowing non-constant types like std::string as in the above snippet ?

The reason you can't do this is because non-constant expressions can't be parsed and substituted during compile-time. They could change during runtime, which would require the generation of a new template during runtime, which isn't possible because templates are a compile-time concept.
Here's what the standard allows for non-type template parameters (14.1 [temp.param] p4):
A non-type template-parameter shall have one of the following (optionally cv-qualified) types:
integral or enumeration type,
pointer to object or pointer to function,
lvalue reference to object or lvalue reference to function,
pointer to member,
std::nullptr_t.

That is not allowed.
However, this is allowed:
template <std::string * temp> //pointer to object
void f();
template <std::string & temp> //reference to object
void g();
See §14.1/6,7,8 in C++ Standard (2003).
Illustration:
template <std::string * temp> //pointer to object
void f()
{
cout << *temp << endl;
}
template <std::string & temp> //reference to object
void g()
{
cout << temp << endl;
temp += "...appended some string";
}
std::string s; //must not be local as it must have external linkage!
int main() {
s = "can assign values locally";
f<&s>();
g<s>();
cout << s << endl;
return 0;
}
Output:
can assign values locally
can assign values locally
can assign values locally...appended some string

You need to be able to mangle template arguments
template <std::string temp>
void f() {
// ...
}
f<"foo">();
f<"foo">(); // same function?
Now an impl would need to come up with a unique sequence of characters for a std::string or, for that matter, any other arbitrary user defined class, storing a particular value, the meaning of which is not known to the implementation. And in addition, the value of arbitrary class objects can't be calculated at compile time.
It's planned to consider allowing literal class types as template parameter types for post-C++0x (see below), which are initialized by constant expressions. Those could be mangled by having the data members recursively mangled according to their values (for base classes, for example we can apply depth-first, left-to-right traversal). But it's definitely not going to work for arbitrary classes.
As of C++20, we are now allowed to use structural class types as template parameters. In a nutshell, structural classes must have a constexpr constructor, destructor and only structural-type members and base classes (like scalars, arrays thereof or references). They must also only have public and non-mutable base classes and members. These provisions, if the template is instantiated with a constant expression converted to the parameter type, allow the compiler to mangle the argument meaningfully.

A non-type template argument provided within a template argument list is an expression whose value can be determined at compile time. Such arguments must be:
constant expressions, addresses of
functions or objects with external
linkage, or addresses of static class
members.
Also, string literals are objects with internal linkage, so you can't use them as template arguments. You cannot use a global pointer, either. Floating-point literals are not allowed, given the obvious possibility of rounding-off errors.

Related

How to define default value for a ctor in template [duplicate]

I understand that non-type template parameters should be a constant integral expression. Can someone shed light why this is so?
template <std::string temp>
void foo()
{
// ...
}
error C2993: 'std::string' : illegal type for non-type template parameter 'temp'.
I understand what a constant integral expression is. What are the reasons for not allowing non-constant types like std::string as in the above snippet ?
The reason you can't do this is because non-constant expressions can't be parsed and substituted during compile-time. They could change during runtime, which would require the generation of a new template during runtime, which isn't possible because templates are a compile-time concept.
Here's what the standard allows for non-type template parameters (14.1 [temp.param] p4):
A non-type template-parameter shall have one of the following (optionally cv-qualified) types:
integral or enumeration type,
pointer to object or pointer to function,
lvalue reference to object or lvalue reference to function,
pointer to member,
std::nullptr_t.
That is not allowed.
However, this is allowed:
template <std::string * temp> //pointer to object
void f();
template <std::string & temp> //reference to object
void g();
See §14.1/6,7,8 in C++ Standard (2003).
Illustration:
template <std::string * temp> //pointer to object
void f()
{
cout << *temp << endl;
}
template <std::string & temp> //reference to object
void g()
{
cout << temp << endl;
temp += "...appended some string";
}
std::string s; //must not be local as it must have external linkage!
int main() {
s = "can assign values locally";
f<&s>();
g<s>();
cout << s << endl;
return 0;
}
Output:
can assign values locally
can assign values locally
can assign values locally...appended some string
You need to be able to mangle template arguments
template <std::string temp>
void f() {
// ...
}
f<"foo">();
f<"foo">(); // same function?
Now an impl would need to come up with a unique sequence of characters for a std::string or, for that matter, any other arbitrary user defined class, storing a particular value, the meaning of which is not known to the implementation. And in addition, the value of arbitrary class objects can't be calculated at compile time.
It's planned to consider allowing literal class types as template parameter types for post-C++0x (see below), which are initialized by constant expressions. Those could be mangled by having the data members recursively mangled according to their values (for base classes, for example we can apply depth-first, left-to-right traversal). But it's definitely not going to work for arbitrary classes.
As of C++20, we are now allowed to use structural class types as template parameters. In a nutshell, structural classes must have a constexpr constructor, destructor and only structural-type members and base classes (like scalars, arrays thereof or references). They must also only have public and non-mutable base classes and members. These provisions, if the template is instantiated with a constant expression converted to the parameter type, allow the compiler to mangle the argument meaningfully.
A non-type template argument provided within a template argument list is an expression whose value can be determined at compile time. Such arguments must be:
constant expressions, addresses of
functions or objects with external
linkage, or addresses of static class
members.
Also, string literals are objects with internal linkage, so you can't use them as template arguments. You cannot use a global pointer, either. Floating-point literals are not allowed, given the obvious possibility of rounding-off errors.

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.

Template Class Specialization for Object Arguments

template<typename T>
class A { };
U x;
A<U> ... // OK
A<x> ... // ERROR
If we can, then how do we make a specialization of class A whose argument is an object of any type?
Before considering "argument is an object of any type", we should first understand the limitations for "argument is an object". (See also How to use an object instance as template argument?)
A clarification before getting into this: What's the difference between an argument and a parameter? A template parameter is the placeholder used in a definition, such as this question's <typename T>. An argument is the replacement provided when the definition is used, such as this question's <U>.
Objects as template parameters
Technically, a template parameter that is neither a type nor a template cannot be an object of class type, but it can be a (lvalue) reference to an object. C++20 relaxes this a bit but not all the way to "any type", so I'll ignore this caveat for now.
In practice, converting a parameter from being an object to being a reference is just a bit of syntax juggling. Still, there is an important bit of semantics to this: different objects produce different template instantiations, even if you believe the objects to be "equal". If you ignore this point, you might needlessly bloat your code. Are you sure this is consistent with your goal?
Objects as template arguments
A template argument that is neither a type nor a template must be a compile-time constant. This is a refrain that is commonly heard when discussing templates. However, it has a perhaps surprising implication when dealing with references. In order for an object reference to be a compile-time constant expression, it must refer to an object with static storage duration. That is, the object must be:
declared at namespace scope,
declared with static, or
declared with extern.
This should make sense if you think about it. A reference needs the address of the object to which it refers. Only an object that is allocated when the program begins has an address that is known at compile time. The address of an object local to a function depends on where in the call stack that particular function call lies, which is a run-time quality.
While a template parameter could be a reference to an object of any type, it is not true that any object of any type could be used as the corresponding argument.
Template parameter of any type
Subject to the above restrictions, allowing an object of any type is just a matter of allowing an object and allowing a type. There are two things that can vary. One of these can hide behind the "auto" keyword.
template<const auto & Object>
class A {};
If you want to be more restrictive about which types are accepted (such as objects of class type, as opposed to objects of fundamental type), there are various type properties that can be used with SFINAE.
Example:
template<const auto & Object>
class A {};
// A class for demonstration purposes
class U {};
// A global variable (declared at namespace scope)
U x;
int main()
{
// A static variable
static U y;
// A local variable
U z;
// Try to instantiate the template
A<x> compiles;
A<y> fine;
//A<z> fails;
// Suppress unused variable warnings
(void) compiles;
(void) fine;
(void) z;
}
Now that you know you could do this, stop to think if you should. You probably should not. Maybe it's just a bit of over-engineering? Trying to be "cool"? How bad could it get?

Instantiating a template class with NULL argument

I understand from bunch of other Stackoverflow threads (like this) that Template arguments are evaluated at Compile Time.
Also, non-type template-parameter should be either a constant expression, integral expression or pointer to an object with external linkage.
And, I am not using --std=c++0x in my g++ command in Makefile.
So, is it possible to instantiate a template class with NULL passed as argument?
// I have a template class like this -
template<class T, T invalidVal, int e> class A
{
static inline bool dummy(T value)
{
return 0;
}
}
#define MY_INVALID_VAL ((void *)0)
// Now I want create class from the above template class with
// T=void*, invalidVal=NULL & e=0
typedef A<void *, MY_INVALID_VAL, 1> ClassA;
The above code compiles fine on MS Visual Studio 2008.
On g++ - I get the error - "a cast to a type other than an integral or enumeration type cannot appear in a constant-expression"
I tried out a few options after googling -
Declare "extern void *MY_INVALID_VAL;" in a header file - include it and do
void MY_INVALID_VAL=NULL; before template instantiation.
In that case, I get error "MY_INVALID_VAL is not a valid template argument for type 'void' because it is not a constant pointer"
So my question is -
Is there no way of instantiating a template class with NULL argument without using c++0x standard?
Thanks!
EDIT:
Thanks for all the comments and thanks for citing exact section from standards' draft.
Just listing down the things I tried -
1) Passing "0" directly doesn't work.
Error I get is - "could not convert '0' to template argument void *"
2) Declaring static const void *my_null=0; and passing my_null doesn't work.
It gave error - "my_null can not appear in constant expression"
3) Tried the pointer to null Object (null object pattern) approach suggested in one of the comments
See below -
class my_null
{
public:
my_null() { my_null_ptr = NULL; }
void * get() { return my_null_ptr; }
private:
void *my_null_ptr;
};
my_null my_null_obj;
my_null *ptr = &my_null_obj;
typedef A<void *, (void *)ptr, 1> ClassA;
Still I get error - "ptr can not appear in constant expression"
So now this has me wondering - what value should I pass to make it work?
Or is there no possible to make it work? (I mean a way that does not involve using c++11 std)
I haven't -yet- found a value that will succeed the compilation.
Any help appreciated (and needed :-P)!
As a sidenote, one more thing that I would want to ask is - is there any pointer value that I can use for non-type template argument?
C++98 says that non-type template arguments shall be one of
an integral constant-expression of integral or enumeration type; or
the name of a non-type template-parameter; or
the name of an object or function with external linkage, including function templates and function template-ids but excluding non-static class members, expressed as id-expression; or
the address of an object or function with external linkage, including function templates and function template-ids but excluding non-static class members, expressed as &id-expression where the & is option if the name refers to a function or array; or
a pointer to member expressed as described in 5.3.1.
Null pointers do not fall under any item of this list, and therefore it is not valid to pass a null pointer as a non-type template parameter.
C++11 updates this list to
for a non-type template-parameter of integral or enumeration type, a converted constant expression (5.19) of the type of the template-parameter; or
the name of a non-type template-parameter; or
a constant expression (5.19) that designates the address of an object with static storage duration and external or internal linkage or a function with external or internal linkage, including function templates and function template-ids but excluding non-static class members, expressed (ignoring parentheses) as & id-expression, except that the & may be omitted if the name refers to a function or array and shall be omitted if the corresponding template-parameter is a reference; or
a constant expression that evaluates to a null pointer value (4.10); or
a constant expression that evaluates to a null member pointer value (4.11); or
a pointer to member expressed as described in 5.3.1.
The updated requirements do cover null pointers. Therefore, to use null pointers as non-type template arguments you must use C++11.
Try this:
static const void* my_null_pointer = 0;
typedef A<void *, my_null_pointer, 1> ClassA;
Something like that worked for me on MSVC and gcc as well.
Passing 0 as that template parameter should work as well.
typedef A<void *, 0, 1> ClassA;

Non-type template parameters

I understand that non-type template parameters should be a constant integral expression. Can someone shed light why this is so?
template <std::string temp>
void foo()
{
// ...
}
error C2993: 'std::string' : illegal type for non-type template parameter 'temp'.
I understand what a constant integral expression is. What are the reasons for not allowing non-constant types like std::string as in the above snippet ?
The reason you can't do this is because non-constant expressions can't be parsed and substituted during compile-time. They could change during runtime, which would require the generation of a new template during runtime, which isn't possible because templates are a compile-time concept.
Here's what the standard allows for non-type template parameters (14.1 [temp.param] p4):
A non-type template-parameter shall have one of the following (optionally cv-qualified) types:
integral or enumeration type,
pointer to object or pointer to function,
lvalue reference to object or lvalue reference to function,
pointer to member,
std::nullptr_t.
That is not allowed.
However, this is allowed:
template <std::string * temp> //pointer to object
void f();
template <std::string & temp> //reference to object
void g();
See §14.1/6,7,8 in C++ Standard (2003).
Illustration:
template <std::string * temp> //pointer to object
void f()
{
cout << *temp << endl;
}
template <std::string & temp> //reference to object
void g()
{
cout << temp << endl;
temp += "...appended some string";
}
std::string s; //must not be local as it must have external linkage!
int main() {
s = "can assign values locally";
f<&s>();
g<s>();
cout << s << endl;
return 0;
}
Output:
can assign values locally
can assign values locally
can assign values locally...appended some string
You need to be able to mangle template arguments
template <std::string temp>
void f() {
// ...
}
f<"foo">();
f<"foo">(); // same function?
Now an impl would need to come up with a unique sequence of characters for a std::string or, for that matter, any other arbitrary user defined class, storing a particular value, the meaning of which is not known to the implementation. And in addition, the value of arbitrary class objects can't be calculated at compile time.
It's planned to consider allowing literal class types as template parameter types for post-C++0x (see below), which are initialized by constant expressions. Those could be mangled by having the data members recursively mangled according to their values (for base classes, for example we can apply depth-first, left-to-right traversal). But it's definitely not going to work for arbitrary classes.
As of C++20, we are now allowed to use structural class types as template parameters. In a nutshell, structural classes must have a constexpr constructor, destructor and only structural-type members and base classes (like scalars, arrays thereof or references). They must also only have public and non-mutable base classes and members. These provisions, if the template is instantiated with a constant expression converted to the parameter type, allow the compiler to mangle the argument meaningfully.
A non-type template argument provided within a template argument list is an expression whose value can be determined at compile time. Such arguments must be:
constant expressions, addresses of
functions or objects with external
linkage, or addresses of static class
members.
Also, string literals are objects with internal linkage, so you can't use them as template arguments. You cannot use a global pointer, either. Floating-point literals are not allowed, given the obvious possibility of rounding-off errors.