Consider this simple function
template<typename T>
void func(const T& x) {std::cout<< typeid(T).name();}
now if I call function func("ddd") , what does T deduces to? . If there were no const in func's parameter , T would be simply char [4] , Whats confusing me is addition of const , what does T deduces to ?
is it : const char [4] . If I change the parameter to T const &x (i.e change order of const) does deduction produces T to char const [4] ?
Can anyone explain argument deduction with string literals?
String literals are arrays of const characters.
A reference to string literal of 4 chars is of type char const (&)[4].
const char [4] and char const [4] are same types!
char const (&)[N], const char [N] and char const [N] all deduce to char const [N]
#include <iostream>
template<typename T>
void func1(T& x) {std::cout<< typeid(T).name()<<std::endl;}
template<typename T>
void func2(const T& x) {std::cout<< typeid(T).name()<<std::endl;}
template<typename T>
void func3(T const &x) {std::cout<< typeid(T).name()<<std::endl;}
int main()
{
char c[4]= {'a','b','c','d'};
const char c1[4]= {'a','b','c','d'};
char const c2[4]= {'a','b','c','d'};
func1("abcd"); //prints char const [4]
func1(c); //prints char [4]
func1(c1); //prints char const [4]
func1(c2); //prints char const [4]
func2("abcd"); //prints char const [4]
func2(c); //prints char [4]
func2(c1); //prints char const [4]
func2(c2); //prints char const [4]
func3("abcd"); //prints char const [4]
func3(c); //prints char [4]
func3(c1); //prints char const [4]
func3(c2); //prints char const [4]
return 0;
}
const usually refers to whatever is in front of it:
char const * -- pointer to constant char
char * const -- constant pointer to char
char const * const -- constant pointer to constant char
char Foo::bar() const -- constant member function returning char
etc.
The leading const is an exception dating back to the early days of C:
const char * <=> char const *
So, changing the ordering in your template would not change the type at all (T const & <=> const T &).
If you want to be consistent about const placement, use the trailing type exclusively, because it's the only way you can place const consistently.
now if I call function func() , what does T deduces to?
If you call with a string literal, then T deduces to char const[N].
If there were no const in func's parameter , T would be simply char [4]
If you mean that if you declared the parameter as T& x, then no: T would still be deduced to char const[N] if you call with a string literal.
If you mean that you call func with a non-const array, then yes: T would be deduced non-const.
If I change the parameter to T const &x
That changes nothing because const T & is just another way of writing T const &.
Related
In the first call, when I pass a char const [] into a template function with a parameter of T const a, T is deduced as char const * which is reasonable because const refers to the decaying pointer.
However, when the parameter type is changed to T const & a, T is deduced as char[7]. From the point of view above, why doesn't the const qualify the whole array type?
template <typename T>
void show1(T const a) {
// input is const char *
// T is char const *
// a is char const * const
}
template <typename T>
void show2(T const & a) {
// input is char const [7]
// T is char[7]
// a is char const (&)[7]
}
int main() {
const char s[] = "asdasd";
show1(s);
show2(s);
}
why doesn't the const qualify the whole array type
Because for array type,
(emphasis mine)
Applying cv-qualifiers to an array type (through typedef or template type manipulation) applies the qualifiers to the element type, but any array type whose elements are of cv-qualified type is considered to have the same cv-qualification.
// a and b have the same const-qualified type "array of 5 const char"
typedef const char CC;
CC a[5] = {};
typedef char CA[5];
const CA b = {};
That means when T is char[7] T const leads to the type char const[7], then T const& (i.e. a's type) is char const (&)[7].
On the other hand, when you pass the array s with type const char[7], the array is considered as const-qualified too. So given the parameter type T const&, T is deduced as char[7] (but not char const[7]).
This is because arrays are non-copyable and non-assignable in C++.
So in calling show1, the const char[] type decays to a const char*. The language permits one implicit conversion per parameter at a function call site.
With show2, you are passing by reference - no copy or assignment is required, so pointer decay does not occur.
template <typename T>
void show(ParameterType a) {
}
show(expression);
Compiler uses expression to deduce T and ParameterType. T and ParameterType are different if ParameterType contains qualifiers like const.
If ParameterType is neither pointer nor reference (case of your show1, T const), type of T is type of expression without const, volatile and reference. So T is type of const char *. ParameterType (type of a) is const char * const.
If ParameterType (T const & in your show2) is pointer or reference (but not reference like T&&). First ignore reference, which gives results T const (same as const T). Second match type of expression (const char []) to const T, so T is char [].
For more information, Item 1 of Effective Modern C++ by Scott Meyers is exactly what you want. The rule is more complicated then which I described here but very important.
I've been trying to pass a method as a pointer function so I created a binder like is shown here but as the method is defined I'm unable to pass it as a parameter to the binder. The function that I need to pass the method pointer is from a Regex Lua Pattern library for arduino found here.
void InterpreterClass::init()
{
MatchState ms("255.255.255.255");
bind_regex_member<InterpreterClass, &InterpreterClass::MatchAddressCallback, 0> b(this);
ms.GlobalMatch("(%d%d?%d?)", b);
}
void InterpreterClass::MatchAddressCallback(const char * match, const unsigned int length, const MatchState & ms)
{
//do something
}
at ms.GlobalMatch the second parameter is the method I want to execute after the string is interpreted, the problem is that the function needs to obey a specific sequence of parameters like if it was "delegated".
typedef void (*GlobalMatchCallback) (const char * match, // matching string (not null-terminated)
const unsigned int length, // length of matching string
const MatchState & ms); // MatchState in use (to get captures)
I tried to implement the binder with all parameters declared and with it's type name declared as well. Bellow follows the binder:
template<class T, void(T::*PTR)(const char *, const unsigned int, const MatchState &), size_t I>
struct bind_regex_member
{
typedef void(*fn_type)(const char *, const unsigned int, const MatchState &);
explicit bind_regex_member(const T* _ptr)
{
ptr = _ptr;
}
static void func(const char * match, const unsigned int length, const MatchState & ms)
{
(ptr->*PTR)(match, length, ms);
}
operator fn_type()
{
return &func;
}
private:
static const T* ptr;
};
template<class T, void(T::*PTR)(const char *, const unsigned int, const MatchState &), size_t I>
const T* bind_regex_member<T, PTR, I>::ptr = NULL;
The first error that the compiler shows is:
Error: `Interpreter.cpp:7:80: error: could not convert template argument ‘&InterpreterClass::MatchAddressCallback’ to ‘void (InterpreterClass::*)(const char*, unsigned int, const MatchState&)’`
Making a binder to GlobalMatchCallback is not working either. What should I do to MatchAddressCallback be called?
Project's minimal code repo: https://github.com/rsegecin/RegexArduino.git.
PS:I found very difficult to express myself with this type of problem so any feedback is welcomed.
The problem here is that ptr in bind_regex_member points to const T and your method InterpreterClass::MatchAddressCallback is non const.
Basicly like this:
InterpreterClass i;
const InterpreterClass* myPtr = &i;
MatchState myMs;
myPtr->MatchAddressCallback("", 0, myMs); // OUCH! myPtr points to const T and MatchAddressCallback is non const member function
Remove the const from ptr in bind_regex_member and it should work!
EDIT:
There is a second problem in Interpreter.h:
class Interpreter
{
public:
void init();
GlobalMatchCallback MatchAddressCallback; // <----------- HERE
};
You cannot declare a method like this. The "final" Interpreter.h should look like this:
#ifndef _INTERPRETER_h
#define _INTERPRETER_h
#include <Arduino.h>
#include "Regexp.h"
template<class T, void(T::*PTR)(const char *, const unsigned int, const MatchState &), size_t I>
struct bind_regex_member
{
typedef void(*fn_type)(const char *, const unsigned int, const MatchState &);
explicit bind_regex_member(T* _ptr)
{
ptr = _ptr;
}
static void func(const char * match, const unsigned int length, const MatchState & ms)
{
(ptr->*PTR)(match, length, ms);
}
operator fn_type()
{
return &func;
}
private:
static T* ptr;
};
template<class T,
void(T::*PTR)(const char *, const unsigned int, const MatchState &), size_t I>
T* bind_regex_member<T, PTR, I>::ptr = NULL;
class InterpreterClass
{
public:
void init();
void MatchAddressCallback(const char * match, const unsigned int length, const MatchState & ms);
};
extern InterpreterClass Interpreter;
#endif
template <typename T>
void f(T t)
{}
int x = 1;
const int & rx = x;
const int * px = &x;
f(rx); // t is int
f(px); // t is const int *, instead of int *, WHY???
I'm confused now. According to Effective Modern c++,
It’s important to recognize that const is ignored only for by-value
parameters. As we’ve seen, for parameters that are references-to- or
pointers-to-const, the constness of expr is preserved during type
deduction.
I think it means
template <typename T>
void f(T * t)
{}
f(px); // t is const int *
template <typename T>
void f(T & t)
{}
f(cx); // t is const int &
template <typename T>
void f(T t)
{}
f(value); // const or volatile of value will be ignored when the type of the parameter t is deduced
So I think when f(px) above, t should be int *, but in fact it is const int *.
Why the const of reference is ignored but the const of pointer isn't?
Or, why isn't rx const int &?
So I think when f(px) above, px should be int *, but in fact it is const int *.
The point is the type of parameter, behaviors change for passed-by-value and passed-by-reference/pointer.
When passed-by-value, the constness of the argument itself is ignored. For a pointer parameter, the constness of pointer is ignored (const pointer -> pointer), but the constness of pointee is still preserved (pointer to const -> pointer to const).
It does make sense because when pass a pointer, the pointer itself is copied, but the pointee is the same, they're both pointing to the same thing, so constness of pointee is preserved; from the point of callers, they won't want the object to be modified, they might use it later. While pass a reference (reference doesn't matter in fact here), you'll get a brand new value copied, which has nothing to do with the original one, then constness is ignored.
As the following explanations in the book, when const pointer to const (a const char* const) passed,
template<typename T>
void f(T param); // param is still passed by value
const char* const ptr = // ptr is const pointer to const object
"Fun with pointers";
f(ptr); // pass arg of type const char * const
The type deduced for param will be const char*.
Only top-level const/volatile qualifiers are ignored. All others are inherent qualities of your type. In other words, you're copying a pointer - it means the function operates on a copy and any modifications to it (like assigning another variable's address) do not modify the original pointer. But if you pass a pointer to const int, having the function modify the integer is very much counter-intuitive.
template <typename T>
void f(T t)
{
t = &another_variable; // ok
}
f(px);
and
void f(T t)
{
*t = 42; // not ok!
}
f(px); // secretly modifying `x` through a pointer to const...
Reference: this answer for pointer to const vs. const pointer differences
I have a simple issue with ctor overload resolution for a class template:
#include <iostream>
#include <string>
using namespace std;
enum EnumTypeVal { READ, WRITE };
template <class T>
class TemplateClassTest {
public:
TemplateClassTest(const string & stringVal, T typeVal, EnumTypeVal e = READ,
const string & defaultStringVal = "default");
TemplateClassTest(const string & stringVal, const char * charVal);
TemplateClassTest(const string & stringVal, EnumTypeVal e = READ,
const string & defaultStringVal = "default");
private:
T type;
};
template <class T>
TemplateClassTest<T>::TemplateClassTest(const string & stringVal, T typeVal,
EnumTypeVal e,
const string & defaultStringVal)
{
type = typeVal;
cout << "In TemplateClassTest(const string &, T, EnumTypeVal, "
"const string &)" << endl;
}
template <class T>
TemplateClassTest<T>::TemplateClassTest(const string & stringVal,
const char * charVal)
{
cout << "In TemplateClassTest(const string &, const char *)" << endl;
}
template <class T>
TemplateClassTest<T>::TemplateClassTest(const string & stringVal, EnumTypeVal e,
const string & defaultStringVal)
{
cout << "In TemplateClassTest(const string &, EnumTypeVal, const string &)"
<< endl;
}
typedef TemplateClassTest<long long unsigned int> u32Type;
typedef TemplateClassTest<bool> boolType;
int main()
{
u32Type l("test", "0"); //matches ctor 2
u32Type v("test", 0); // ambiguity between ctor 1 and 2
boolType b("test", "true"); //matches ctor 2
return 0;
}
The second call fails to compile by throwing error:
Call of overloaded 'TemplateClassTest(const char [5], int) is ambiguous.
Why does int matches to const char *? This situation can be solved by changing the const char * to const string & in ctor 2. But doing so, boolType b("test", "true") now gets matched to ctor 1 instead of ctor 2.
My requirements are:
u32Type v("test", 0) should match ctor 1
boolType b("test", "true") should match ctor 2.
Limitations are:
ctor 1 and 3 signatures can't be changed
user code calls in main() can't be changed.
Any help is highly appreciated..Thanks!
It is ambiguous because 0 is a null pointer constant. A null pointer constant can be implicitly converted to any pointer type -> a null pointer conversion. Therefore, ctor 2 is viable: TemplateClassTest(const string&, const char*).
As an integer literal, 0 is of type int. Constructor 1, TemplateClassTest(const string&, T, EnumTypeVal = READ, const string & = "default"), therefore requires a conversion from int to unsigned long long -> an integral conversion.
Those two conversions, null pointer conversion and integral conversion, have the same rank (Conversion), hence the ambiguity.
[conv.ptr]/1
A null pointer constant is an integral constant expression prvalue of integer type that evaluates to zero or a prvalue of type std::nullptr_t. A null pointer constant can be converted to a pointer type; the result is the null pointer value of that type and is distinguishable from every other value of object pointer or function pointer type. Such a conversion is called a null pointer conversion.
A possible, but ugly fix that matches your constraints is to change the second constructor to:
template<class U,
class V = typename std::enable_if<std::is_same<U, const char*>{}>::type>
TemplateClassTest(const string & stringVal, U charVal);
That is, a greedy constructor template, restricted by SFINAE to only accept const char* as the second argument. This heavily restricts the implicit conversions applied to the second argument when trying to match this ctor.
The out-of-line definition becomes:
template <class T>
template<class U, class V>
TemplateClassTest<T>::TemplateClassTest(const string & stringVal, U charVal)
{
cout << "In TemplateClassTest(const string &, const char *)" << endl;
}
Could anyone explain why the code does not compile.
template<class T, class DER>
struct Base {
T a;
Base(const T argB) : a(argB){}
};
template<class T>
struct Derived : Base<T, Derived<T> > {
Derived(const T argD) : Base<T, Derived<T> >(argD){}
};
int main() {
int val = 10;
const int *p = &val;
/* this was in the original question
Derived<int*> d(p); // breaks, but compiles with Derived<const int*> d(p);
*/
Derived d(p); // fails, but Derived<const int*> d(p); compiles
}
The error message is that about no conversion from int* to const int*. As I see it T can be substitues by int* and in that case the constructor to Derived receives its argument as a const int* and invokes the base with const int*. Why then is the constant qulaification getting lost.
I clearly do not understand how template argument deduction works. I have not been able to find any lucid but rigorous and exhaustive description of how it works when const, * and & are in play. That is, what will a get type deduced to in these various cases.
Foo(T& a)
Foo(T a)
Foo(T* a)
Foo(const T a)
Foo(const T*a)
Foo(const t&a)
when a is
an object,
a pointer and
an array.
Because the constructor of Derived is Derived(const T argD), so in your case it is Derived(int * const). This does not accept an const int*.
A "const (pointer to int)" is not a "(pointer to const int)".
There is no template deduction in your example. Derived<int*> d(p); specifically sets the template parameter to T to int* (pointer to int). Your derived constructor takes a const T parameter which is a const pointer to int in this case. I think your confusion is because const int* p; does not declare a const pointer to int, but instead declares a pointer to const int which is not, and cannot be converted to, a const pointer to int (the former lets you modify the pointed to value while the latter does not).
Remember that C and C++ declarations are generally read from the variable name outwards, so for const int* p you start at p, go left and see * and then go farther left and see const int, so p is a pointer to a const int. Here is a good guide on deciphering C declarations and cdecl is also a very useful tool.
The problem with your example is p is a pointer to const int, but the constructor of Derived<int*> takes a const pointer to int since T is int*. This may seem confusing, but you can think of const as having a higher precedence than * in type declarations. So in const int * the const applies to int and then * applies to the whole thing making p a pointer to const int whereas for const T, the const applies to T, which is actually int* so const T argD makes argD a const pointer to int.
Using this same idea all your Foo examples can be easily deciphered.
Foo(T& a) // a is a reference to a value of type T
Foo(T a) // a is a value of type T
Foo(T* a) // a is a pointer to a value of type T
Foo(const T a) // a is a constant value of type T
Foo(const T* a) // a is a pointer to a constant value of type T
Foo(const T& a) // a is a reference to a constant value of type T
In general only Foo(T a) and Foo(const T a) cannot be overloaded because it doesn't matter to the caller whether the argument is copied into a constant variable or not.
More specifically, if T is char * (pointer to char)
Foo(char *&a) // a is a reference to a pointer to a char
Foo(char *a) // a is a pointer to a char (*)
Foo(char **a) // a is a pointer to a pointer to a char
Foo(char *const a) // a is a constant pointer to a char (cannot overload with (*))
Foo(char *const *a) // a is a pointer to a constant pointer to a char
Foo(char *const &a) // a is a reference to a constant pointer to a char
If T is const char* (pointer to a const char) things are much the same
Foo(const char *&a) // a is a reference to a pointer to a const char
Foo(const char *a) // a is a pointer to a const char (*)
Foo(const char **a) // a is a pointer to a pointer to a const char
Foo(const char *const a) // a is a constant pointer to a const char (cannot overload with (*))
Foo(const char *const *a) // a is a pointer to a constant pointer to a const char
Foo(char *const &a) // a is a reference to a constant pointer to a const char
If T is char* const (const pointer to a char) then all the const T overloads are redundant because const T is equivalent to T when T is already const.
Foo(char *const &a) // a is a reference to a const pointer to a char (+)
Foo(char *const a) // a is a const pointer to a char (*)
Foo(char *const *a) // a is a pointer to a const pointer to a char (^)
Foo(char *const a) // a is a const pointer to a char (same as (*))
Foo(char *const *a) // a is a pointer to a const pointer to a char (same as (^))
Foo(char *const &a) // a is a reference to a const pointer to a char (same as (+))