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;
Related
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.
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.
When answering this question, I tried the following code with gcc (code compiled) and clang (code rejected):
typedef long (*func)(int);
long function(int) { return 42; }
struct Test
{
static constexpr func f = &function;
};
template<func c>
struct Call
{
static void f()
{
c(0);
}
};
int main()
{
Call<Test::f>::f();
}
I am not sure which compiler is right, although I think the constexpr initialization of Test::f is ok. The error clang outputs is:
error: non-type template argument for template parameter of pointer type 'func'
(aka 'long (*)(int)') must have its address taken
Which compiler is right ?
If clang is right, why , and what does this error really means ?
EDIT: for the "why", see DyP's question.
14.3.2 Template non-type arguments [temp.arg.nontype]
A template-argument for a non-type, non-template template-parameter shall be one of:
[...]
— 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; [...]
(n3485, emphasis mine)
I don't know exactly why it's been limited, but I think it might be related to the fact that the function address is not available at compile time (there might be a substitute for template instantiation purposes).
Edit: Enhanced answer due to a follow-up question (comment) of Synxis
constexpr func = &function;
^ this is well-formed; you can use the address of a function to initialize a constexpr object.
The problem is that it's explicitly forbidden to use pointers as non-type template arguments other than of the form &identifier:
using My_Call = Call < &function >; // fine
constexpr func mypointer = &function; // fine
using My_Ind_Call = Call < func >; // forbidden, argument not of form `&id`
The following code is a simplified minimal version of a feature I am trying to implement for a client requirement.
It fails to compile on IBM's XLC compiler (version 9 and 11, both) with the error A non-type template parameter cannot have type "int X::*".. However, I have tried the same code with g++ 4.7.2, clang++ 3.2 and Intel-13.0, and they compile it successfully.
I am curious to know if XLC is the only sane voice here, or are the other compilers correct?
struct X {
X() : y(123) {}
int y;
};
struct XFoo {
typedef int X::* Type;
};
template <typename Name, typename Name::Type value>
struct Bar {
typename Name::Type getValue(Name) {
return value;
}
};
template class Bar<XFoo, &X::y>; // xlc error here, works fine on others
int main() {}
I read through the C++ 2003 standard chapter on templates several times, and could not conclusively find something which prohibits using <type> <class>::* as a non-template type. I have already searched through SO and search engines for an explanation, but have not come across any authoritative source which helps me decide.
I understand that this may not be a good coding practice, but this is required for the client code since their requirement is somewhat unique. I have also tried out various other alternatives but it does not work for them.
§ 14.1/4 of the C++03 Standard allows having pointers to member as a template parameter:
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,
reference to object or reference to function,
pointer to member.
Correspondingly, per § 14.3.2/1:
A template-argument for a non-type, non-template template-parameter shall be one of:
an integral constant-expression of integral or enumeration type; or
the name of a non-type template-parameter; 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 optional if the name refers to a function or array, or if the corresponding template-parameter is a reference; or
a pointer to member expressed as described in 5.3.1.
Thus, non-type template parameters can be pointers to member. There are some limitations which apply to template specialization, but they don't seem to apply in this case.
Pointer-to-member template parameters are explicitly allowed, XLC should be served a bug report.
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.