Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ passing a const pointer by const reference

Tags:

c++

constants

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.

like image 578
user176168 Avatar asked Apr 22 '13 15:04

user176168


2 Answers

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.

like image 143
David Rodríguez - dribeas Avatar answered Oct 27 '22 22:10

David Rodríguez - dribeas


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.

like image 44
Mark B Avatar answered Oct 27 '22 22:10

Mark B