C++ String-type independent algorithms - c++

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;
};

Related

Strongly typed c++ varargs [duplicate]

This question already has answers here:
C++ parameter pack, constrained to have instances of a single type?
(7 answers)
C++ Multiple function parameters with varargs of a specific type [duplicate]
(2 answers)
Why must C++ function parameter packs be placeholders or pack expansions?
(3 answers)
Can I create a function which takes any number of arguments of the same type?
(3 answers)
Closed 6 months ago.
I'm writing a utility function in c++ 11 that adds an element of a single type to a vector. Most variable argument docs/guides I've found show a template with the typedef type, but I'm looking to only allow a single type for all the variable arguments (const char*). The following is the relevant snippet of code:
Item.hpp:
// Guard removed
#include <vector>
class Item {
public:
Item(const char* n, bool (*optionChange)(uint8_t), const char*...);
private:
std::vector<const char*> options;
void addOption(const char*);
}
Item.cpp:
#include "Item.hpp"
void Item::addOption(const char* option) {
options.push_back(option);
}
Item::Item(
const char* n,
bool (*optionChange)(uint8_t),
const char* opts...
): name(n), type(MENU_TYPE_OPTS), selectedOpt(0) {
addOption(opts...); // Doesn't compile
}
Compilation of the above code fails with the message error: expansion pattern 'opts' contains no argument packs.
Use a variadic template. With variadic template, also all types can be different, but you can request that they are all the same via SFINAE
#include <type_traits>
#include <tuple>
template <typename ...T>
std::enable_if_t<
std::is_same_v< std::tuple<const char*, T...>,
std::tuple<T...,const char*>>
,void>
foo(T...t) {}
int main() {
const char* x;
int y;
foo(x,x,x,x); // OK
foo(x,y,x); // error
}
This is based on a neat trick to check if all types of a variadic pack are the same type (i'll add the referene to the original when I find it). std::tuple<const char*, T...> and std::tuple<T...,const char*> are only the same type when all Ts are const char*. std::enable_if will discard the specialization when the condition (all Ts are const char*) is not met and attempting to call it results in a compiler error.
This is rather old fashioned and works already in C++11 (apart from the _v/_t helpers). I suppose in C++20 there are less arcane ways to require all Ts to be const char*.
I missed that it is a constructor and you cannot do return-type-SFINAE on a constructor. It just needs to be a little more convoluted:
#include <type_traits>
#include <tuple>
struct foo {
template <typename ...T,
std::enable_if_t<
std::is_same_v< std::tuple<const char*, T...>,
std::tuple<T...,const char*>
>,
bool
> = true>
foo(T...t) {}
};
int main() {
const char* x;
int y;
foo f1(x,x,x,x); // OK
foo f2(x,y,x); // error
}
When the condition is met the last template parameter is non-type bool and has a default value of true. It's only purpose is to fail when the condition is not met (hence it does not need to be named).
You can't expand variadic arguments like parameter packs. You either have to switch to another approach (like the parameter packs/std::initializer_list) or rely on variadic functions of <cstdarg>:
Item::Item(const char* n, bool (*optionChange)(uint8_t), size_t opt_num, ...): name(n), type(MENU_TYPE_OPTS), selectedOpt(0) {
va_list args;
va_start(args, opt_num);
for (decltype(opt_num) i = 0; i < opt_num; ++i) {
const auto option = va_arg(args, const char *);
std::cout << option << std::endl;
}
va_end(args);
...
}
Be advised that in this scenario, you don't have control over types of passed arguments, and the entire contract is supposed to be agreed verbally or in comments. The client code also should provide meta-data for the approach to be viable (i.e. in this example opt_num stores the number of strings passed)
EDIT
CPP community definitely doesn't like the varargs thing, as my answer was instantly downvoted, so another, more C++ friendly solution is to replace varargs with std::initializer_list:
Item::Item(const char* n, bool (*optionChange)(uint8_t), std::initializer_list<const char *> options): name(n), type(MENU_TYPE_OPTS), selectedOpt(0) {
for (const auto& option: options){
std::cout << option << std::endl;
}
...
}

Using template to handle string and wstring

I have following two functions:
void bar(const std::string &s)
{
someCFunctionU(s.c_str());
}
void bar(const std::wstring &s)
{
someCFunctionW(s.c_str());
}
Both of these call some C function which accepts const char * or const wchar_t * and have U or W suffixes respectively. I would like to create a template function to handle both of these cases. I tried following attempt:
template <typename T>
void foo(const std::basic_string<T> &s)
{
if constexpr (std::is_same_v<T, char>)
someCFunctionU(s.c_str());
else
someCFunctionW(s.c_str());
}
But this does not seem to work correctly. If I call:
foo("abc");
this will not compile. Why is that? why a compiler is not able to deduce the proper type T to char? Is it possible to create one function which would handle both std::string and std::wstring?
this will not compile. Why is that? why a compiler is not able to deduce the proper type T to char?
As better explained by others, "abc" is a char[4], so is convertible to a std::basic_string<char> but isn't a std::basic_string<char>, so can't be deduced the T type as char for a template function that accept a std::basic_string<T>.
Is it possible to create one function which would handle both std::string and std::wstring?
Yes, it's possible; but what's wrong with your two-function-in-overloading solution?
Anyway, if you really want a single function and if you accept to write a lot of casuistry, I suppose you can write something as follows
template <typename T>
void foo (T const & s)
{
if constexpr ( std::is_same_v<T, std::string> )
someCFunctionU(s.c_str());
else if constexpr ( std::is_convertible_v<T, char const *>)
someCFunctionU(s);
else if constexpr ( std::is_same_v<T, std::wstring> )
someCFunctionW(s.c_str());
else if constexpr ( std::is_convertible_v<T, wchar_t const *> )
someCFunctionW(s);
// else exception ?
}
or, a little more synthetic but less efficient
template <typename T>
void foo (T const & s)
{
if constexpr ( std::is_convertible_v<T, std::string> )
someCFunctionU(std::string{s}.c_str());
else if constexpr (std::is_convertible_v<T, std::wstring> )
someCFunctionW(std::wstring{s}.c_str());
// else exception ?
}
So you should be able to call foo() with std::string, std::wstring, char *, wchar_t *, char[] or wchar_t[].
The issue here is that in foo("abc");, "abc" is not a std::string or a std::wstring, it is a const char[N]. Since it isn't a std::string or a std::wstring the compiler cannot deduce what T should be and it fails to compile. The easiest solution is to use what you already have. The overloads will be considered and it is a better match to convert "abc" to a std::string so it will call that version of the function.
If you want you could use a std::string_view/std::wstring_view instead of std::string/std::wstring so you don't actually allocate any memory if you pass the function a string literal. That would change the overloads to
void bar(std::string_view s)
{
someCFunctionU(s.data());
}
void bar(std::wstring_view s)
{
someCFunctionW(s.data());
}
Do note that std::basic_string_view can be constructed without having a null terminator so it is possible to pass a std::basic_string_view that won't fulfill the null terminated c-string requirement that your C function has. In that case the code has undefined behavior.
A workaround in C++17 is:
template <typename T>
void foo(const T &s)
{
std::basic_string_view sv{s}; // Class template argument deduction
if constexpr (std::is_same_v<typename decltype(sv)::value_type, char>)
someCFunctionU(sv.data());
else
someCFunctionW(sv.data());
}
And to avoid issue mentioned by Justin about non-null-terminated string
template <typename T> struct is_basic_string_view : std::false_type {};
template <typename T> struct is_basic_string_view<basic_string_view<T>> : std::true_type
{};
template <typename T>
std::enable_if_t<!is_basic_string_view<T>::value> foo(const T &s)
{
std::basic_string_view sv{s}; // Class template argument deduction
if constexpr (std::is_same_v<typename decltype(sv)::value_type, char>)
someCFunctionU(sv.data());
else
someCFunctionW(sv.data());
}
Yes, there exist a type, i.e. std::basic_string<char>, which can be copy initialized from expression "abc". So you can call a function like void foo(std::basic_string<char>) with argument "abc".
And no, you can't call a function template template <class T> void foo(const std::basic_string<T> &s) with argument "abc". Because in order to figure out whether the parameter can be initialized by the argument, the compiler need to determine the template parameter T first. It will try to match const std::basic_string<T> & against const char [4]. And it will fail.
The reason why it will fail is because of the template argument deduction rule. The actual rule is very complicated. But in this case, for std::basic_string<char> to be examined during the deduction, compiler will need to look for a proper "converting constructor", i.e. the constructor which can be called implicitly with argument "abc", and such lookup isn't allowed by the standard during deduction.
Yes, it is possible to handle std::string and std::wstring in one function template:
void foo_impl(const std::string &) {}
void foo_impl(const std::wstring &) {}
template <class T>
auto foo(T &&t) {
return foo_impl(std::forward<T>(t));
}

String literal that depends on the template type parameter?

I have a class that can parse strings (dates). I want to be able to parse normal as well as wide strings:
MyClass x;
x.parse("2018-02-27");
x.parse(L"2018-02-27");
Since the code to parse a normal string and a wide string is basically the same, it makes sense to use a template:
template<typename CharT>
void parse(const CharT *str)
{
// ...
}
Now, for parsing I am going to use the get_time function. It takes the fmt parameter, which is of type const CharT *, and for which I would like to provide a string literal. It must be a normal or wide string literal, depending on the template type parameter:
template<typename CharT>
void parse(const CharT *str)
{
tm date;
basic_istringstream<CharT> date_stream{str};
date_stream >> get_time(&date, ("%Y-%m-%d" or L"%Y-%m-%d", but how to choose??) );
// ...
}
I am only interested in two template instantiations: char and wchar_t. I tried to use non-type template parameters, but did not manage to get anything that compiles.
What is the most elegant way to implement the function/template?
Add a traits class:
template <typename CharT>
struct format {
static const CharT* const v;
};
template<> const char* const format<char>::v="%Y-%m-%d";
template<> const wchar_t* const format<wchar_t>::v=L"%Y-%m-%d";
then use as:
date_stream >> get_time(&date, format<CharT>::v);
If you were feeling ambitious, you could merge the actual duplicated format into a #define (and then use token pasting to glue an L on the front where necessary) - but actually, I think that's more machinery than it's worth.
My first attempt to shoehorn if constexpr in there didn't go well, but a variable template looks fine:
template <typename CharT>
constexpr CharT const *timeFmt;
template <>
constexpr auto timeFmt<char> = "%Y-%m-%d";
template <>
constexpr auto timeFmt<wchar_t> = L"%Y-%m-%d";
template <typename CharT>
void parse(const CharT *str)
{
std::tm date;
std::basic_istringstream<CharT> date_stream{str};
date_stream >> std::get_time(&date, timeFmt<CharT>);
// ...
}
For the record, here is the ugly thing that came out of my first try:
template<typename CharT>
void parse(const CharT *str)
{
std::tm date;
std::basic_istringstream<CharT> date_stream{str};
date_stream >> std::get_time(&date, []{
if constexpr (std::is_same_v<CharT, wchar_t>)
return L"%Y-%m-%d";
else
return "%Y-%m-%d";
}());
// ...
}
Besides variable templates and class templates, a function template will do as well:
template<typename T> const T* get_format_str();
Then, the corresponding specializations for both char and wchar_t:
template<> const char* get_format_str<char>() { return "%Y-%m-%d"; }
template<> const wchar_t* get_format_str<wchar_t>() { return L"%Y-%m-%d"; }
Use this function template in your parse() function template as:
date_stream >> get_time(&date, get_format_str<TChar>());
The advantages to this approach are:
It only requires C++98 (for variable templates you need C++14).
The pointer to the literal string can't be modified (only the copy of that pointer returned by the function can be modified).

How to write a traits conversion to add 'const' to type*

Is it possible to have a traits in order to convert, let's say, char* to const char* in order to use it further to call functions having const char* parameters using a char* variable?
I have this (the context is large, I've simplified, sorry for the ugly code):
#include <iostream>
using namespace std;
#include <stdio.h>
#include <string.h>
#include <typeinfo>
template<typename T, typename U, std::enable_if_t<std::is_same<T, U>::value, int> = 0>
T convert_type(U _in)
{
return _in;
}
template<typename T, typename U, std::enable_if_t<std::is_same<T, std::add_lvalue_reference_t<U>>::value, int> = 0>
T& convert_type(U& _in)
{
return _in;
}
template<typename T, typename U, std::enable_if_t<std::is_same<T, std::add_lvalue_reference_t<std::add_const_t<U>>>::value, int> = 0>
T& convert_type(U& _in)
{
return _in;
}
template<typename T, typename U, std::enable_if_t<std::is_same<T, std::add_pointer_t<U>>::value, int> = 0>
T convert_type(U& _in)
{
return std::addressof(_in);
}
int main() {
char* c = new char[sizeof "test"];
strcpy(c, "test");
const char * cc = convert_type<const char *, char *>(c); // here not compilable yet due to lack of the right convert_type specialization
cout << c;
delete[] c;
return 0;
}
Just to clarify, I embed SpiderMonkey to script Illustrator API. This made me write pretty complicated code, but I have checked and I know the conversion traits above are in use for various function calls. I've added them here just to see the approach and to clarify the need of adding another that recognizes a type and returns the needed type. All your comments are correct generally, but not in my context. I might have a convoluted code, but I have simplified it as much as I could.
sample code here
As far as it concerns me, the question is not yet answered and I couldn't find a solution. I put the question this way: how to write a traits method to match char* against const char*, which is char const*, actually, when I check in msvc with typeid a type or variable declared as const char*?
Is it possible to have a traits in order to convert, let's say, char* to const char* in order to use it further to call functions having const char* parameters using a char* variable?
As pointed out in the comments, a traits conversion isn't needed at all, since a char* pointer can be used equally as a const char* pointer at any time, since the conversion is implicit (same for any other type than char).
Note that the line in your main() function
char * c = "test";
isn't valid c++ syntax. "test" actually is a const char [5] type and you can't assign that to other than a const char* pointer legally.
At least any attempt to write a value to that pointer will be undefined behavior.
Most c++11 compliant compilers will issue a warning on that statement.
As for your edited example now
char* c = new char[sizeof "test"];
strcpy(c, "test");
//const char * cc = convert_type<const char *, char *>(c);
there's no need for using the convert_type() traits function, you can simply write (as also mentioned in the comments):
const char * cc = c;
(To 'defend' OP: yes, I also thought of simply adding const, since const const T is the same as const T, but, on the other hand, I can think of situations, where you have to pass a type trait that does nothing else but this.)
How about:
// ordinary way w/o type aliases
template<typename T>
struct const_qualify
{
typedef const T type;
};
// do this if you have type aliases
template<typename T>
using const_qualify_t = const T;
Note that, if you just want to compare types, we have now remove_const<> / remove_cv<>: http://en.cppreference.com/w/cpp/types/remove_cv
EDIT: actually, we also have std::add_const<>.
Just use assignment:
const char* cc = c;
(with the caveat that char* c = "test" is ill-formed)
To be more explicit, which is never a bad thing, you can use one of the standard C++ casts:
auto cc = static_cast<const char*>(c);
Your first three convert_type() overloads are basically useless - all of them can be trivially substituted by simply using =. And = actually handles more cases than your convert_type() too.
The fourth overload should be substituted in favor of just & (or directly using std::addressof), there's really no need to hide that you're taking the address. Certainly T* x = &y is a lot easier to understand the meaning of than T* x = convert_type<T*>(y).
Eventually, I've made it, using this post:
template<typename T> struct remove_all_const : std::remove_const<T> {};
template<typename T> struct remove_all_const<T*> {
typedef typename remove_all_const<T>::type *type;
};
template<typename T> struct remove_all_const<T * const> {
typedef typename remove_all_const<T>::type *type;
};
template<typename T, typename U, std::enable_if_t<std::is_same<typename remove_all_const<T>::type, typename remove_all_const<U>::type>::value, int> = 0>
T convert_type(U& _in)
{
return _in;
}
A big Thank! to that post solver!

C++ passing a const pointer by const reference

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.