This question already has answers here:
Is there any difference between "T" and "const T" in template parameter?
(3 answers)
Closed 9 years ago.
What is the effect of the const keyword in this template?
template <class T, int const ROWNUM, int const COLNUM>
class Matrix
Does it mean that this template only accept a const as parameter? If so, is there a way to pass a variable as the COLNUM and ROWNUM?
(when I try to pass a variable as the COLNUM for the template, it gives an error: "IntelliSense: expression must have a constant value")
It's ignored:
[C++11: 14.1/4]: 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.
[C++11: 14.1/5]: [ Note: Other types are disallowed either explicitly below or implicitly by the rules governing the form of template-arguments (14.3). —end note ] The top-level cv-qualifiers on the template-parameter are ignored when determining its type.
The same wording is present at the same location in C++03.
This is partially because template arguments must be known at compile-time anyway. So, whether you have the const there or not, you may not pass some variable value:
template <int N>
void f()
{
N = 42;
}
template <int const N>
void g()
{
N = 42;
}
int main()
{
f<0>();
g<0>();
static const int h = 1;
f<h>();
g<h>();
}
prog.cpp: In function ‘void f() [with int N = 0]’:
prog.cpp:15: instantiated from here
prog.cpp:4: error: lvalue required as left operand of assignment
prog.cpp: In function ‘void g() [with int N = 0]’:
prog.cpp:16: instantiated from here
prog.cpp:10: error: lvalue required as left operand of assignment
prog.cpp: In function ‘void f() [with int N = 1]’:
prog.cpp:19: instantiated from here
prog.cpp:4: error: lvalue required as left operand of assignment
prog.cpp: In function ‘void g() [with int N = 1]’:
prog.cpp:20: instantiated from here
prog.cpp:10: error: lvalue required as left operand of assignment
const is not required in your case
for instance, both classes Matrix_A and Matrix_B below are the same for the compiler point of view. const here is just to enforce the fact that ROWNUM and COLNUM are constant for humans point of view, but not required.
template <class T, int const ROWNUM, int const COLNUM>
class Matrix_A
{
};
template <class T, int ROWNUM, int COLNUM>
class Matrix_B
{
};
Moreover following class Matrix_C also specify similar constant variables ROWNUM and COLNUM in another way:
template <class T>
class Matrix_C
{
static int const ROWNUM = 5;
static int const COLNUM = 20;
};
// the following three objects use constant variables ROWNUM and COLNUM
Matrix_A<bool,5,20> a;
Matrix_B<bool,5,20> b;
Matrix_C<bool> c;
Related
Consider the following example:
template <class T>
void f(const T&&) { std::cout << __PRETTY_FUNCTION__; };
int main(void){
const int *cptr = nullptr;
f(std::move(cptr));
}
Per [temp.deduct.call]/1:
Template argument deduction is done by comparing each function
template parameter type (call it P) that contains
template-parameters that participate in template argument deduction
with the type of the corresponding argument of the call (call it A)
[..]
and [temp.deduct.call]/3:
If P is a cv-qualified type, the top-level cv-qualifiers of P's type
are ignored for type deduction. If P is a reference type, the type
referred to by P is used for type deduction.
Considering the given paragraphs, I'm deducing the template argument for T to be int* as follows:
P = const T&&, A = const int*; // replacing 'const T&&' with 'const T'
P = const T, A = const int*
T = int*
// what's wrong with these steps?
But when I compile this code with gcc and clang, it shows that the deduced T is const int*
My Question: Why the deduced template argument is const int* and not int* as I expect?
Why the deduced template argument is const int* and not int* as I expect?
The const in P = const T is a top level const and applies to T while the const in A = const int* is a low-level const meaning it does not apply to the pointer. This in turn means that you can't directly compare const T with const int* the way you have done.
Therefore, T will be deduced as the simplest argument type const int* and the function parameter will be of type const int *const &&.
In following code:
template<typename T>
struct TD;
decltype(auto) getMyConst() {
const int val = 10;
return (const int)val; // const int cast is not needed here actually
}
int main()
{
TD<decltype(getMyConst())> type_test;
}
produces error:
main.cpp:41:32: error: implicit instantiation of undefined template 'TD<int>'
TD<decltype(getMyConst())> type_test;
which indicates that deduced type is int, I would expect decltype rules to deduce const int. Why does clang compiler deduce 'int'?
In an attempt to call a function template which accepts a type and a parameter/argument of that type as the template parameters/arguments, compiler gives an error which is not produced with similar parameters/arguments. So I was wondering what is the correct parameters/arguments in case of calling the function templates for the member function "operator[]const" of a vector class!
Consider this piece of code:
class test_class{
public:
int member;
int& operator[](size_t) {return member;}
const int& operator[](size_t) const{return member;}
};
typedef std::vector<int> vector_type;
typedef const int&(test_class::* OK_type)(size_t)const;
typedef const int&(vector_type::* not_OK_type)(size_t)const;
static constexpr OK_type OK_pointer = &test_class::operator[];
static constexpr not_OK_type not_OK_pointer = &vector_type::operator[];
template<typename t__, t__>
void function(){}
The above code is alright now consider the main function:
int main() {
function<OK_type, OK_pointer>();
function<not_OK_type, not_OK_pointer>();
return 0;
}
The first call of the function template is OK but not the second one.
The error which compiler produce is:
error: no matching function for call to ‘function<not_OK_type, not_OK_pointer>()’
note: candidate: ‘template<class t__, t__ <anonymous> > void function()’
note: template argument deduction/substitution failed:
error: ‘const int& (std::vector<int>::*)(size_t) const{((const int& (std::vector<int>::*)(size_t) const)std::vector<int>::operator[]), 0}’ is not a valid template argument for type ‘const int& (std::vector<int>::*)(long unsigned int) const’
function<not_OK_type, not_OK_pointer>();
note: it must be a pointer-to-member of the form ‘&X::Y’
Interestingly even if the function template was formed as:
template<auto>
void function(){}
it would cause the same result.
I must add that in the case of non const version, error is the same (for std::vector).
So I am wondering
A: what is wrong?
B: Considering that if there was a mismatch between the not_OK_type and &vector_type::operator[], then compiler would also give an error in case of:
static constexpr not_OK_type not_OK_pointer = &vector_type::operator[];
Is there a difference between types that can be used as constexpr and the type that can be used as a template parameter/argument?
The issue is typedef const int&(vector_type::* not_OK_type)(size_t)const;.
If you see stl_vector.h (here), the operator[] is declared as noexcept at line 1040.
But in the declaration of not_OK_type variable, noexcept is not present. That's why the compiler complains.
For getting rid of compilation error, add noexcept to not_OK_type variable. Like this:
typedef const int&(vector_type::* not_OK_type)(size_t)const noexcept;
Working code:
#include <vector>
class test_class{
public:
int member;
int& operator[](size_t) {return member;}
const int& operator[](size_t) const{return member;}
};
typedef std::vector<int> vector_type;
typedef const int&(test_class::* OK_type)(size_t)const;
typedef const int&(vector_type::* not_OK_type)(size_t)const noexcept;
static constexpr OK_type OK_pointer = &test_class::operator[];
static constexpr not_OK_type not_OK_pointer = &vector_type::operator[];
template<typename t__, t__>
void function(){}
int main() {
function<OK_type, OK_pointer>();
function<not_OK_type, not_OK_pointer>();
return 0;
}
#Kunal Puri, provided a correct answer for the the section A of the question.
As for the section B of the question, I guess the clue can be found in one exception that applies when converted constant expression used as template argument for a non-type template parameter.
According to (https://en.cppreference.com/w/cpp/language/constant_expression), a converted constant expression under certain condition and specifically in case of conversion of pointer to noexcept function to pointer to function is a constant expression. Which explains why compiler did not produce an error in case of:
static constexpr not_OK_type not_OK_pointer = &vector_type::operator[];
However according to (https://en.cppreference.com/w/cpp/language/template_parameters), this type of converted constant expression can not be used as a pointer to non-static data members(and maybe also a pointer to non-static member functions) for a non-type template parameter.
This exception might be the source of conflict between types that can be used as constexpr and as template argument, although statements in the later source are vague and not directly linked to the case.
Consider the following example code:
#include <array>
struct MyClass
{
size_t value = 0;
constexpr static size_t size() noexcept
{
return 3;
}
};
template <size_t N>
void DoIt()
{
MyClass h;
std::array<int, h.size()> arr;
}
int main()
{
DoIt<1>();
}
When I try to compile this with GCC 7.3.0, I get an error about h not being usable in a non-constexpr context:
cexpr.cpp: In function ‘void DoIt()’:
cexpr.cpp:17:26: error: the value of ‘h’ is not usable in a constant expression
std::array<int, h.size()> arr;
^
cexpr.cpp:16:11: note: ‘h’ was not declared ‘constexpr’
MyClass h;
^
cexpr.cpp:17:27: error: the value of ‘h’ is not usable in a constant expression
std::array<int, h.size()> arr;
^
cexpr.cpp:16:11: note: ‘h’ was not declared ‘constexpr’
MyClass h;
^
However, when I try compiling the exact same code in Clang 6.0.0, it compiles without any errors. Additionally, when I modify the code to not be inside the templated DoIt() function, GCC compiles this just fine:
#include <array>
struct MyClass
{
size_t value = 0;
constexpr static size_t size() noexcept
{
return 3;
}
};
int main()
{
MyClass h;
// this compiles just fine in Clang and GCC
std::array<int, h.size()> arr;
}
I already know how to fix the first code so it compiles on GCC using decltype, but I'm curious to know why doesn't the first piece of code compile with GCC? Is this just a bug in GCC, or is there something I don't understand about using constexpr static member functions?
Looks like a bug to me.
The type and meaning of the expression h.size() is defined by [expr.ref] "Class member access":
[expr.post]/3
Abbreviating postfix-expression.id-expression as E1.E2, E1 is called the object expression. [...]
and
[expr.post]/6.3.1
If E2 is a (possibly overloaded) member function, function overload resolution is used to determine whether E1.E2 refers to a static or a non-static member function.
(6.3.1) If it refers to a static member function and the type of E2 is “function of parameter-type-list returning T”, then E1.E2 is an lvalue; the expression designates the static member function. The type of E1.E2 is the same type as that of E2, namely “function of parameter-type-list returning T”.
This means h.size has the same type as ::MyClass::size and is evaluated as such, regardless of the fact that h is constexpr or not.
h.size() is then a call to a constexpr function and is a core constant expression according to [expr.const]/4.
template<typename T>
void fun(T& param) // param is a reference
{
T abc;
abc=1;//cannot change abc value because T is deduced as const int
//It gives compilation error because T is deduced as const int
}
template<typename T>
void fun1(const T& param) // param is a reference
{
T abc;
abc=1;//can change abc value because T is deduced as int
//why T is deduced int here
}
int main()
{
int x = 2; // x is an int
const int cx = x; // cx is a const int
const int& rx = x; // rx is a reference to x as a const int
fun(x); // T is int, param's type is int&
fun(cx); // T is const int, param's type is const int&
fun(rx); // T is const int,param's type is const int&
fun1(x); // T is int, param's type is int&
fun1(cx); // T is int, param's type is const int&
fun1(rx); // T is int,param's type is const int&
return 0;
}
updated
Why const int is not deduced for T in case of fun1 template function(when function template parameter type is const lvalue reference) even const int cx and const int& rx is passed while instantiation but const int(T) type is deduced for fun(function template)?
Does T type deduction is depend on function template param(parameter) type.
When you have a template function like
template<typename T>
void fun(T & param)
if the parameter that is passed is actually const, or volatile, then in the template instantiation, T will also be "cv-qualified" appropriately.
When you put the cv-qualifier in the template function declaration, like so,
template<typename T>
void fun(const T & param)
then T is not bound as const. But it will be volatile if the parameter volatile.
It's actually very intuitive -- T is the simplest type that will make the function call work, i.e., make the expected type of the function argument match what was passed.
Similarly, if my function is
template <typename T>
void fun(T * param)
then if I pass it an int *, T will be bound as int.
This is described in great detail in the C++11 standard [temp.deduct.call](14.8.2.1), see part three about the CV-qualifiers:
[temp.deduct.call] (14.8.2.1)
(1) Template argument deduction is done by comparing each function template parameter type (call it P) with
the type of the corresponding argument of the call (call it A) as described below. ...
(2) If P is not a reference type:
(2.1) — If A is an array type, the pointer type produced by the array-to-pointer standard conversion (4.2) is
used in place of A for type deduction; otherwise,
(2.2) — If A is a function type, the pointer type produced by the function-to-pointer standard conversion (4.3)
is used in place of A for type deduction; otherwise,
(2.3) — If A is a cv-qualified type, the top level cv-qualifiers of A’s type are ignored for type deduction.
(3) If P is a cv-qualified type, the top level cv-qualifiers of P’s type are ignored for type deduction. If P is a
reference type, the type referred to by P is used for type deduction.
Since cx and rx exhibit the same behavior here, we will use only cx.
cx is const int.
case of fun:
T& matches against const int so T is deduced as const int, because const is not part of T &. The type of param is const int&.
case of fun1:
const T& matches against const int so T is deduced as int, because const already is in const T&. The type of param is const int&.
Let's have it all visually horizontally aligned by type match, maybe this is more clear:
fun param type: | T | &
receives : | const int |
fun1 param type: | const | T | &
receives : | const | int |