How template argument deduction is performed in this example? - c++

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 &&.

Related

How to enforce constness over template type?

template<typename T> struct SomeClass{
void someFunc(const T& data) const {}
};
void testFunc(const int* a) {
SomeClass<int*> some_class;
some_class.someFunc( a);
}
I made a template instance with a non-const type. Now when calling a certain function I get errors that say:
error: invalid conversion from ‘const int*’ to ‘int*’
note: initializing argument 1 of ‘void SomeClass<T>::someFunc(const T&) const [with T = int*]’
So basically my const T& is treated as plain T&, the const is ignored. Why? How can I make sure in this case that it is seen by the compiler as const T&?
You may want to consider to partial specialize your class template SomeClass for the case T is a pointer. Then, add const to the type pointed to instead of the pointer itself (i.e., pointer to const instead of const pointer):
template<typename T> struct SomeClass<T*> {
void someFunc(const T* &data) const { /* ... */ }
};
SomeClass<int*>::someFunc() (i.e., T = int*) will be instantiated to:
void someFunc(const int* &data) const;
data above is a reference to a pointer to const int. However, with your primary template, SomeClass<int*>::someFunc() is actually:
void someFunc(int* const &data) const;
That is, data here is a reference to a const pointer to int. Therefore, you can't pass a, which is a const int*, as an argument to someFunc() since that pointed const int would be modifiable through the parameter data. In other words, the constness would be lost.
You need to change your definition to SomeClass<const int*> some_class;. The T comes from the definition and is int*, compiler is complaining rightfully.
The const is not ignored. It's applied to the type int*, yielding an int* that cannot be modified, i.e., int* const. In const int*, the const applies to the int, not to the pointer. That is, const int* points at an int that cannot be modified.
Inside testFunc you end up both consts. Since it's called with a const int*, the specialization of SomeClass has to be SomeClass<const int*>. And then when you call someFunc you get the second one; the actual argument type is const int* const. The first const applies to the int and the second const applies to the argument itself, i.e., to the pointer.
Assuming that the code base is huge and therefore you can't afford to write a specialization for your class template, you could provide the following delegating member template, someFunc(), which is an overload of your original member function:
#include <type_traits>
template<typename T> struct SomeClass {
// your original member function
void someFunc(const T &data) const { /* ... a lot of stuff ... */ }
// delegating member template
template<typename S>
void someFunc(const S* &data) const {
// delegate to original function
someFunc(const_cast<S*>(data));
}
};
First, this member template only comes into play with pointer arguments. Second, what it really does is to delegate the call to your original member function with the same name by casting out the const from the pointed type.
I hope it helps.

How std::size_t is calculated in the template code for array type

I was reading the book C++ templates - the complete guide, 2nd edition and got the code from that which looks like this:-
template<typename T>
void showVal(const T &arg1, const T &arg2)
{
std::cout << arg1 << arg2;
}
int main() {
showVal("hello", "world1");
return 0;
}
The above code gave me this error:- "error C2782: 'void showVal(const T &,const T &)': template parameter 'T' is ambiguous". This is reasonable because the arguments I am passing are deduced to const char[6] and const char[7]. To fix this, I have made the changes in the function which look like this after the change :-
template<typename T, std::size_t L1, std::size_t L2>
void showVal(const T (&arg1)[L1], const T(& arg2)[L2])
{
std::cout << arg1 << arg2;
}
PS:- I've got this fix from the book
The main confusion underlies in the value of L1 and L2. How compiler knows that it has to pass 6 and 7 to the template parameter L1 and L2. Is there any rule for array type.
The type of "hello" is const char[6], as shown in the error message of your first attempt. The length of the array is 6. As you can see, the length of the array is part of the type of the array. Since the compiler has to know the type, it implicitly also knows the length.
Just like the template type argument T was deduced to be const char based on the type of the expressions passed to the non-template arguments arg1 and arg2 (those types being const char[6] and const char[7]), so too the template non-type arguments L1 and L2 were deduced from those same parameter types.
function template
template<typename T, std::size_t L1, std::size_t L2>
void showVal(const T (&arg1)[L1], const T(& arg2)[L2])
for call showVal("hello", "world1") compiler will make implicit instantiation
showVal(const char (&arg1)[6], const T(& arg2)[7])
and that is the way you can prevent that array decays to pointer, binding reference to array type, you achieve that array size of arguments is known to function definition.
so for call showVal("hello", "world1"), compiler deduced T is char and arguments type are array of char size 6 and 7 respectively.

Type deduction with template, method pointer and strings

I have encounter serious template type deduction problem when I use method pointer in argument of a template function.
Let's take the following code:
template <class ClassT, typename Arg1T>
inline void testTemplateFct(ClassT * clazz,
void (ClassT::*fctPtr)(Arg1T),
const Arg1T & arg1)
{
}
class TestClass
{
public:
void testMethodIntArg(int arg)
{}
void testMethodDoubleArg(double arg)
{}
void testMethodStringArg(const char * arg);
};
int main()
{
TestClass testClass;
testTemplateFct(&testClass,
&TestClass::testMethodIntArg,
10);
testTemplateFct(&testClass,
&TestClass::testMethodDoubleArg,
10.0);
/// BEGINNING OF MY PROBLEM
testTemplateFct(&testClass,
&TestClass::testMethodStringArg,
"a string...");
/// END OF MY PROBLEM
return 0;
}
If I compile it using g++, I get the following error message:
$ g++ ArgumentDeduction.cpp -o ArgumentDeduction
ArgumentDeduction.cpp: In function ‘int main()’:
ArgumentDeduction.cpp:42:18: error: no matching function for call to ‘testTemplateFct(TestClass*, void (TestClass::*)(const char*), const char [12])’
"a string...");
^
ArgumentDeduction.cpp:4:13: note: candidate: template<class ClassT, class Arg1T> void testTemplateFct(ClassT*, void (ClassT::*)(Arg1T), const Arg1T&)
inline void testTemplateFct(ClassT * clazz,
^~~~~~~~~~~~~~~
ArgumentDeduction.cpp:4:13: note: template argument deduction/substitution failed:
ArgumentDeduction.cpp:42:18: note: deduced conflicting types for parameter ‘const Arg1T’ (‘const char*’ and ‘char [12]’)
"a string...");
If I remove the reference of the third argument of method testTemplateFct the problem disappears (HOWEVER I ABSOLUTELY NEED THE REFERENCE IN ORDER TO AVOID COPY)
template <class ClassT, typename Arg1T>
inline void testTemplateFct(ClassT * clazz,
void (ClassT::*fctPtr)(Arg1T),
const Arg1T arg1)
{}
I understand more or less the error message but I do not understand why there is an ambiguity between const char* and char [12]. I do not understand why the problem disappears when I remove the reference.
Finally, I would strongly appreciate any help in order to correct this code while keeping the reference
PS: I know that I can "force" the type deduction by doing:
testTemplateFct(&testClass,
&TestClass::testMethodStringArg,
(const char *) "a string...");
but I don't like it very much
Your template requires that both the occurrences of Arg1T are deduced to the same type. I believe that is not what you want. Instead the types should be deduced independently:
template <class ClassT, typename Arg1T, typename GivenT>
inline void testTemplateFct(ClassT * clazz,
void (ClassT::*fctPtr)(Arg1T),
GivenT &&arg1)
{
//example use
(clazz->*fctPtr)(std::forward<GivenT>(arg1));
}
I do not understand why there is an ambiguity between const char* and char [12].
Note that "a string..." is an array with type const char[12]. For the function template testTemplateFct, the parameter arg1 is declared as a reference, i.e. const Arg1T &, then array-to-pointer decay won't occur in template argument deduction and Arg1T is deduced as char[12], which doesn't match the deduced type of Arg1T from the 2nd argument, i.e. const char*, so deduction failed.
I do not understand why the problem disappears when I remove the reference.
When the parameter is declared as pass-by-value array-to-pointer decay is applied; then both the deduced type of Arg1T from the 2nd and 3rd argument will be const char* and everything work fine.
You have two basic options.
The first one is to change your invocation to:
testTemplateFct(&testClass,
&TestClass::testMethodStringArg,
(const char *)"a string...");
The second option is to add an overload:
template <class ClassT, size_t n>
inline void testTemplateFct(ClassT * clazz,
void (ClassT::*fctPtr)(const char *),
const char (&arg1)[n])
{
testTemplateFct<ClassT, const char *>(clazz, fctPtr, arg1);
}
Pick which one works best for you.
A literal character string is actually a const char[n], and not a const char *. The const char array decays to a const char * in an ordinary function call; but this decay does not occur as part of template deduction; hence the problem.

How function template type deduction works when parameter type is const lvalue reference vs non-const lvalue refernce in c++11

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 |

const in template argument [duplicate]

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;