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 (+))
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
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 &.
Hmm a strange one in VC2012 I can't seem to work out the syntax for passing a const pointer by const reference into a function of a templated class whose template argument is a non const pointer ie:
template<typename T>
struct Foo
{
void Add( const T& Bar ) { printf(Bar); }
};
void main()
{
Foo<char*> foo;
const char* name = "FooBar";
foo.Add(name); // Causes error
}
So I've simplified my problem here but basically I want the argument to 'Add' to have a const T ie const char*. I've tried:
void Add( const (const T)& Bar );
typedef const T ConstT;
void Add( const (ConstT)& Bar );
void Add( const typename std::add_const<T>::type& Bar );
None of which work. The exact error I'm getting is:
error C2664: 'Foo<T>::Add' : cannot convert parameter 1 from 'const char *' to 'char *const &'
with
[
T=char *
]
Conversion loses qualifiers
which I can see is correct but how do I solve it without const casting 'name' to be non const.
There is a strong difference between a pointer to a constant object (T const*, or const T*) and a constant pointer to a non-constant object (T * const). In your case the signature of the member Add is:
void Foo<char *>::Add(char * const& ); // reference to a constant pointer to a
// non-constant char
I usually recommend that people drop the use of const on the left hand side exactly for this reason, as beginners usually confuse typedefs (or deduced types) with type substitution and when they read:
const T& [T == char*]
They misinterpret
const char*&
If the const is placed in the right place:
T const &
Things are simpler for beginners, as plain mental substitution works:
char * const &
A different problem than what you are asking, but maybe what you think you want, is:
Given a type T have a function that takes a U that is const T if T is not a pointer type, or X const * if T is a pointer to X
template <typename T>
struct add_const_here_or_there {
typedef T const type;
};
template <typename T>
struct add_const_here_or_there<T*> {
typedef T const * type;
};
Then you can use this in your signature:
template <typename T>
void Foo<T>::Add( const typename add_const_here_or_there<T>::type & arg ) {
...
Note that I am adding two const in the signature, so in your case char* will map to char const * const &, as it seems that you want to pass a const& to something and you also want the pointed type to be const.
You might have wondered as of the name for the metafunction: *add_const_here_or_there*, it is like that for a reason: there is no simple way of describing what you are trying to do, which is usually a code smell. But here you have your solution.
It looks like your issue here as that as soon as you have a pointer type mapped to a template type, you can no longer add const-ness to the pointed-to type, only to the pointer itself. What it looks like you're trying to do is automatically add constness to the parameter of your function (so if T is char* the function should accept const char* const& rather than char* const& as you've written). The only way to do that is with another template to add constness to the pointee for pointer types, as follows. I took the liberty of including missing headers and correcting the signature of main:
#include <cstdio>
template<typename T>
struct add_const_to_pointee
{
typedef T type;
};
template <typename T>
struct add_const_to_pointee<T*>
{
typedef const T* type;
};
template<typename T>
struct Foo
{
void Add( typename add_const_to_pointee<T>::type const & Bar ) { printf(Bar); }
};
int main()
{
Foo<char*> foo;
const char* name = "FooBar";
foo.Add(name); // Causes error
}
As mentioned in another another however, this issue just goes away if you use std::string instead of C-style strings.
You need to change the template argument to your Foo object to Foo<const char*>. Because if T=char*, then const T=char*const, not const char*. Trying to coerce it to work is not a good idea and would probably result in undefined behavior.
Use:
Foo<const char*> foo;
const char* name = "FooBar";
foo.Add(name);
And write int main() instead of void main()
If passing const char* instead of char* to Foo is not an option you can finesse the correct type with std::remove_pointer. This will remove the pointer modifier and allow you to provide a more explicit type.
#include <type_traits>
template<typename T>
struct Foo
{
void Add(typename std::remove_pointer<T>::type const*& Bar ) { printf(Bar); }
};
To prevent the pointer value from being modified you can declare the reference as const as well.
void Add(typename std::remove_pointer<T>::type const* const& Bar )
{ Bar = "name"; } // <- fails
If you need to reduce the type from say a pointer to pointer you can use std::decay along with std::remove_pointer
void Add(typename std::remove_pointer<typename std::decay<T>::type>::type const*& Bar)
{
printf(Bar);
}
This really depends on what your requirements for T are. I suggest assuming only the base type (e.g. char) is passed as T and building reference and pointer types from that.