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.
Related
A string like "hello" would be string or const char*.
Consider an example:
template<typename A>
A s(A a){
// ...
}
here what would be "hello" converted to, if I called s("hello")?
A string like "hello" is a const char[6]. Since you can't pass an array by value, A will be deduced to const char* instead.
When you are looking for a type you may use this trick :
Create a struct without implementation
template<typename A>
struct Error;
And use it :
template<typename A>
A s(A a){
Error<A> error;
return a;
}
int main()
{
Error<decltype("error")> e; // error: implicit instantiation of undefined template 'Error<char const (&)[6]>'
s("hello"); // error: implicit instantiation of undefined template 'Error<const char *>'
}
The error will give you the type you are looking for.
Tada! "Hello" type is char const [6] but in the s the decuce type is const char *
Credit :
Effective Modern C++, Chapter 1. Deducing Types, Item 4: Know how to view deduced types.
https://www.oreilly.com/library/view/effective-modern-c/9781491908419/ch01.html
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
now i have
template<class C> class Array
{
inline int Search(const C &value) const;
...
}
i want to use in this way
Array<int *> a;
int i;
const int *pi = &i;
a.Search(pi);
but i got
error C2664: 'A::Search' : cannot convert parameter 1 from 'const
int *' to 'int *const &'
is it possible to workaround it somehow?
Only i can think for now it is to partially specialize this claxx for pointers, but i'm not sure.
template<class C> class Array<C*>
{
inline int Search(const C *&value) const;
...
}
Is it good way, maybe it is possible to do it without creating partial specialization?
As you have noticed, your problem comes from the fact that you're adding a top-level const to your template parameter, but when it comes to pointers it yields T * const not T const *.
Specializing your template is one way to do achieve what you want.
Another way would be to make a helper template that takes care of the deep "constification" and use this in your main template. This is usually easier to do it that way because it means less code duplication. Something along the lines of:
template<typename C>
struct Constify {
typedef const C type;
};
template<typename C>
struct Constify<C*> {
typedef const C* const type;
};
template<class C>
class Array
{
inline int Search(typename Constify<C>::type & value) const;
...
};
I have been struggling with this one for about a half day and it seem that at least XCode 4.6 has a bug where certain declaration of template class would violate the language and allow to pass const data from within class to external functions with parameters delcared as without const modifiers.
The example below WILL COMPILE, even tough template declaration of
Tcaller::call() method specifies const arguments passed as reference
but static cmp function is providing useless *const & modifiers.
template< typename T> struct Tcalled
{
// !!!error - this prototype doesn't protect the data passed to function
// because it should be declared with const modifiers but it wouldn't compile then.
// SEE: Below my NOTE for correct function prototype.
static bool cmp(const Tcalled*& item, const int& key) //<- correct but doesn't work
static bool cmp(Tcalled* const & item, const int& key) //<- invalid but works!!
{
return (item->index = key); /// error - we modify const object here !
}
T index;
};
template < typename T> struct Tcaller
{
Tcaller(){}
template < typename K, bool (*compare)(const T& item, const K& key) >
bool call(int k) const { return compare(data, k); }
T data;
};
int main(int argc, char *argv[])
{
const Tcaller<Tcalled<int>* > tmp; // <- const data
int k = 1;
tmp.call<int,Tcalled<int>::cmp>(k); //call here WILL modify const data !!
}
And the question : HOW I can force XCode to obey the rules and allow me to prototype
my static function as it was declared for template parameter ? As for now these are the errors from XCode I get when I declare my static method correctly :
No matching member function for call to 'call'
Candidate template ignored: invalid explicitly-specified argument for template parameter 'compare'
Thanks!
Presumably you mean that it works when the argument is declared as Tcalled<T>* const & item and the body of cmp should be using == not =.
You have a misunderstanding about the way template arguments are instantiated. It's not just a copy-paste substitution of the template arguments. You're expecting that const T& when instantiated with T as Tcalled<int>* will be equivalent to const Tcalled<int>*&; that is, a "reference to pointer to const Tcalled<int>.
However, this is wrong, the const applies to the whole T type. So really, after instantiation, const T& is equivalent to Tcalled<int>* const&. This is why having the argument declared as Tcalled* const & item works fine.
To get this to work with the declaration as const Tcalled<T>*& item, a number of things have to change:
The call function template arguments should be defined like so:
template < int (*compare)(T& item) >
That is, the function pointer type takes a T&, not a const T&. This makes sense as the cmp function does not take a reference to const object at all (it takes a reference to non-const pointer).
The call function should not be const:
int call() { return compare(data); }
This is because it's passing its member T to compare, which is a reference to non-const object (the pointer itself is not const). It can't do that if it is a const function because it cannot guarantee that compare won't modify the object.
Tcaller must be instantiated with T as const Tcalled<int>*:
Tcaller<const Tcalled<int>* > tmp;
I'm trying to derive a technique for writing string-algorithms that is truly independent of the underlying type of string.
Background: the prototypes for GetIndexOf and FindOneOf are either overloaded or templated variations on:
int GetIndexOf(const char * pszInner, const char * pszString);
const char * FindOneOf(const char * pszString, const char * pszSetOfChars);
This issue comes up in the following template function:
// return index of, or -1, the first occurrence of any given char in target
template <typename T>
inline int FindIndexOfOneOf(const T * str, const T * pszSearchChars)
{
return GetIndexOf(FindOneOf(str, pszSearchChars), str);
}
Objectives:
1. I would like this code to work for CStringT<>, const char *, const wchar_t * (and should be trivial to extend to std::string)
2. I don't want to pass anything by copy (only by const & or const *)
In an attempt to solve these two objectives, I thought I might be able to use a type-selector of sorts to derive the correct interfaces on the fly:
namespace details {
template <typename T>
struct char_type_of
{
// typedef T type; error for invalid types (i.e. anything for which there is not a specialization)
};
template <>
struct char_type_of<const char *>
{
typedef char type;
};
template <>
struct char_type_of<const wchar_t *>
{
typedef wchar_t type;
};
template <>
struct char_type_of<CStringA>
{
typedef CStringA::XCHAR type;
};
template <>
struct char_type_of<CStringW>
{
typedef CStringW::XCHAR type;
};
}
#define CHARTYPEOF(T) typename details::char_type_of<T>::type
Which allows:
template <typename T>
inline int FindIndexOfOneOf(T str, const CHARTYPEOF(T) * pszSearchChars)
{
return GetIndexOf(FindOneOf(str, pszSearchChars), str);
}
This should guarantee that the second argument is passed as const *, and should not determine T (rather only the first argument should determine T).
But the problem with this approach is that T, when str is a CStringT<>, is a copy of the CStringT<> rather than a reference to it: hence we have an unnecessary copy.
Trying to rewrite the above as:
template <typename T>
inline int FindIndexOfOneOf(T & str, const CHARTYPEOF(T) * pszSearchChars)
{
return GetIndexOf(FindOneOf(str, pszSearchChars), str);
}
Makes it impossible for the compiler (VS2008) to generate a correct instance of FindIndexOfOneOf<> for:
FindIndexOfOneOf(_T("abc"), _T("def"));
error C2893: Failed to specialize function template 'int FindIndexOfOneOf(T &,const details::char_type_of<T>::type *)'
With the following template arguments: 'const char [4]'
This is a generic problem I've had with templates since they were introduced (yes, I'm that old): That it's been essentially impossible to construct a way to handle both old C-style arrays and newer class based entities (perhaps best highlighted by const char [4] vs. CString<> &).
The STL/std library "solved" this issue (if one can really call it solving) by instead using pairs of iterators everywhere instead of a reference to the thing itself. I could go this route, except it sucks IMO, and I don't want to have to litter my code with two-arguments everywhere a single argument properly handled should have been.
Basically, I'm interested in an approach - such as using some sort of stringy_traits - that would allow me to write GetIndexOfOneOf<> (and other similar template functions) where the argument is the string (not a pair of (being, end] arguments), and the template that is then generated be correct based on that string-argument-type (either const * or const CString<> &).
So the Question: How might I write FindIndexOfOneOf<> such that its arguments can be any of the following without ever creating a copy of the underlying arguments:
1. FindIndexOfOneOf(_T("abc"), _T("def"));
2. CString str; FindIndexOfOneOf(str, _T("def"));
3. CString str; FindIndexOfOneOf(T("abc"), str);
3. CString str; FindIndexOfOneOf(str, str);
Related threads to this one that have lead me to this point:
A better way to declare a char-type appropriate CString<>
Templated string literals
Try this.
#include <type_traits>
inline int FindIndexOfOneOf(T& str, const typename char_type_of<typename std::decay<T>::type>::type* pszSearchChars)
The problem is that when you make the first argument a reference type T becomes deduced as:
const char []
but you want
const char*
You can use the following to make this conversion.
std::decay<T>::type
The documentation says.
If is_array<U>::value is true, the modified-type type is remove_extent<U>::type *.
You can use Boost's enable_if and type_traits for this:
#include <boost/type_traits.hpp>
#include <boost/utility/enable_if.hpp>
// Just for convenience
using boost::enable_if;
using boost::disable_if;
using boost::is_same;
// Version for C strings takes param #1 by value
template <typename T>
inline typename enable_if<is_same<T, const char*>, int>::type
FindIndexOfOneOf(T str, const CHARTYPEOF(T) * pszSearchChars)
{
return GetIndexOf(FindOneOf(str, pszSearchChars), str);
}
// Version for other types takes param #1 by ref
template <typename T>
inline typename disable_if<is_same<T, const char*>, int>::type
FindIndexOfOneOf(T& str, const CHARTYPEOF(T) * pszSearchChars)
{
return GetIndexOf(FindOneOf(str, pszSearchChars), str);
}
You should probably expand the first case to handle both char and wchar_t strings, which you can do using or_ from Boost's MPL library.
I would also recommend making the version that takes a reference take a const reference instead. This just avoids instantiation of 2 separate versions of the code (as it stands, T will be inferred as a const type for const objects, and a non-const type for non-const objects; changing the parameter type to T const& str means T will always be inferred as a non-const type).
Based on your comments about iterators it seems you've not fully considered options you may have. I can't do anything about personal preference, but then again...IMHO it shouldn't be a formidable obstacle to overcome in order to accept a reasonable solution, which should be weighed and balanced technically.
template < typename Iter >
void my_iter_fun(Iter start, Iter end)
{
...
}
template < typename T >
void my_string_interface(T str)
{
my_iter_fun(str.begin(), str.end());
}
template < typename T >
void my_string_interface(T* chars)
{
my_iter_fun(chars, chars + strlen(chars));
}
Alternative to my previous answer, if you don't want to install tr1.
Add the following template specializations to cover the deduced T type when the first argument is a reference.
template<unsigned int N>
struct char_type_of<const wchar_t[N]>
{
typedef wchar_t type;
};
template<unsigned int N>
struct char_type_of<const char[N]>
{
typedef char type;
};