I have a "set" data type:
template <class V>
struct Set {
void add(const V& value) {}
};
I want to write a top-level function version of Set::add.
template <class V>
void add(const Set<V>& set, const V& value) {}
This doesn't quite work with string literals:
Set<const char*> set;
const char* val = "a";
set.add(val); // ok
set.add("a"); // ok
add(set, val); // ok
add(set, "a"); // ERROR
add<const char*>(set, "a"); // ok
The error message (g++ 4.2.4):
no matching function for call to ‘add(Set<const char*>&, const char [2])’
It looks it has something to do with the fact that "a" has type const char[2] and not const char*. Does anybody know how to get this to work?
The problem is that V gets one type for the left parameter, and another type for the right one. I suspect you also want to be able to say add(setOfLong, 0) - but with that template you couldn't. I recommend to add a separate template parameter to solve this
template <class SetV, class V>
void add(const Set<SetV>& set, const V& value) {}
There's another way to solve this problem (forget where I saw it...).
You can use an "Identity" type wrapper to have the compiler not take a type into account when performing inference.
template <T>
struct Identity {
typedef T type;
};
Then define "add" like this:
template <class V>
void add(const Set<V>& set, const typename Identity<V>::Type& value) {}
This causes 'V' to be deduced solely based on the first argument type. Once that's determined, it goes ahead and uses it for the second argument, which works fine.
Related
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));
}
I'm experiencing some problems which can be resumed by the following piece of code:
template <typename Key, typename Data, typename fct>
size_t wrapper(const std::pair<Key, Data> & p)
{
return fct(p.first);
}
int main(int argc, char *argv[])
{
size_t val =
wrapper<int, int, dft_hash_fct<int>>(std::pair<int,int>(5,9));
return 0;
}
I'm using clang compiler version 3.4 and this code does not compile with the following error
test-tmp.C:17:5: error: no matching function for call to 'wrapper'
wrapper<int, int, dft_hash_fct<int>>(std::pair<int,int>(5,9));
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
test-tmp.C:9:8: note: candidate template ignored: invalid explicitly-specified argument
for template parameter 'fct'
The idea is to wrap a hash function (the template parameter fct) on std::pair for only taking the first field.
dft_hash_fct is another template defines as follows:
template <typename Key>
size_t dft_hash_fct(const Key & key)
{
return SuperFastHash(key);
}
This generic function works; it has been used in other contexts.
The purpose of all this is to reuse a hash based set (not map) as a map of keys to items of any type. The hash based ser receives a hash function in construction time.
Thanks for your comments (David, Andrey and Kazark)
Edited:
Well, I see, typename fct is a type, so I cannot handle as a pointer function; sorry for the trivia. Unfortunately, I believe that the approach of passing the function as parameter in the wrapper does not work, because the hash set expects a function pointer with the following signature:
size_t (*the_function)(const Key & key);
So, realizing this, thanks to your observations, I changed the code in question to:
template <typename Key, typename Data, size_t (*fct)(const Key & k)>
size_t wrapper(const std::pair<Key, Data> & p)
{
return (*fct)(p.first);
}
int main(int argc, char *argv[])
{
size_t val =
wrapper<int, int, dft_hash_fct<int>>(std::pair<int,int>(5,9));
return 0;
}
that compiles, links and runs. In addition, I put this line:
size_t (*fct)(const std::pair<int, int>&) =
wrapper<int, int, dft_hash_fct<int>>;
cout << (*fct)(std::pair<int, int>(4,6)) << endl;
And that compiles, links ans runs too. So, I can say that the compiler (and of course according to the language) can instantiate the function and handle a function pointer to it.
So, after that I tried to modify my original code, which is a derived class of HAshSet intended for managing pairs hashed by first field.
I declare some as:
template <typename Key, typename Data>
class HashMap : public HashSet<std::pair<Key, Data>>
{
...
HashMap(size_t (*function)(const Key & key))
: HashSet<Key, Data>(wrapper<Key, Data, function>)
{
}
..
};
But the compilation (with std=c++11) fails with the error
./tpl_dynSetHash.H:353:7: error: no matching constructor for initialization of
'HashSet<std::pair<unsigned long, long>>'
: HashSet<std::pair<Key,Data>(
^
testDynSetHash.C:178:8: note: in instantiation of member function
'HashMap<unsigned long, long>::HashMap' requested here
HMap table;
However, if I substitute the call to base constructor by
: HashSet<Key, Data>(wrapper<Key, Data, dft_hash_fct<Key>)
That compiles fine. Thus, I believe that the problem is with the parameter type declaration (but I do not know what is).
The standard idiom to pass functions is to pass them as function objects, e.g.
template <typename Key, typename Data, typename Fct>
size_t wrapper(const std::pair<Key, Data> & p, Fct fct)
{
return fct(p.first);
}
Then call the wrapper using:
int main(int argc, char *argv[])
{
// no explicit template arguments required
size_t val =
wrapper(std::pair<int,int>(5,9), &dft_hash_fct<int>);
return 0;
}
In your code, on the other hand:
template <typename Key, typename Data, typename fct>
size_t wrapper(const std::pair<Key, Data> & p)
{
return fct(p.first);
}
typename fct introduces an alias for a type. Inside this function, fct names a type; therefore fct(p.first) creates an object of type fct, and this object needs to be converted to a size_t in order to return it from wrapper. You can use this as well, but the type you had to use would have to look like this:
struct dft_hash_fct_t
{
size_t result;
dft_hash_fct_t(int p) : result(SuperFashHash(p)) {}
operator size_t() const { return result; }
};
Which is probably not what you intended.
The template declaration in
template <typename Key, typename Data, typename fct>
size_t wrapper(const std::pair<Key, Data> & p)
{
return fct(p.first);
}
declares template parameterfct as a type, but you are trying to pass a function pointer to it. You can make fct function pointer template parameter like this:
template <typename Key, typename Data, size_t(*fct)(const Key&)>
size_t wrapper(const std::pair<Key, Data> & p)
{
return fct(p.first);
}
However, the more idiomatic way is (as DyP says) to pass a function object so that the function works with function pointers as well as objects overloading operator():
template <typename Key, typename Data, typename Fct>
size_t wrapper(const std::pair<Key, Data> & p, Fct fct)
{
return fct(p.first);
}
Then when calling it you pass the function as a parameter
wrapper(std::pair<int,int>(5,9), dft_hash_fct<int>);
The code you wrote makes no sense within the context of your intent. Your fct template parameter is a type. That means that
return fct(p.first);
is a function-style cast, not an application of () operator (i.e. it is not a function call). In your code you are attempting to cast p.first to type fct and then attempting to return the result of that cast as size_t. Was that your intent? I doubt that it was. On top of that you are trying to pass a function pointer value dft_hash_fct<int> as a template argument for fct, i.e. you are passing a value where a type is expected. How did you expect it to work?
The description you provided seems to imply that you actually wanted to call a functor with type fct from inside wrapper instead of performing a cast. In order to do that you have to obtain the functor itself somehow. Remember again that fct is not a functor, its is just the type of the functor.
The typical approach would be to pass the functor from the outside, as function parameter
template <typename Key, typename Data, typename fct>
size_t wrapper(const std::pair<Key, Data> & p, fct f)
{
return f(p.first);
}
Now you can use your wrapper template with class-based functors, as well as with ordinary functions
size_t val = wrapper(std::pair<int,int>(5,9), dft_hash_fct<int>);
Note that dft_hash_fct<int> has to be supplied as function argument, not as template argument.
There's no need to explicitly specify template arguments, since they will be deduced by the compiler.
I have:
A template routine unarchive that takes a dictionary and a key and based on the template type (T) passed can be specialized to produce a T
A constructor for a struct that leverages unarchive to construct its members
An example might be the following:
template <typename T>
T unarchive(const dictionary_t&, key_type key);
struct foo
{
foo(const dictionary& archive) :
value_m(unarchive<decltype(value_m)>(archive, value_key))
{ }
some_value_type value_m;
};
The advantage of using unarchive<decltype(value_m)> here is that I can change the type of value_m without having to update this line of code - the type always follows the member variable's type.
The problem I have is more aesthetic: it is very verbose. Currently I have a macro:
#define UNARCHIVE_FOR(var) unarchive<decltype(var)>
And the foo's constructor changes as follows:
foo(const dictionary& archive) :
value_m(UNARCHIVE_FOR(value_m)(archive, value_key))
{ }
Now I have a result that is more terse but far uglier. Can the same result be achieved without a macro? What I would like would be something akin to:
foo(const dictionary& archive) :
value_m(unarchive<value_m>(archive, value_key))
{ }
How can this be done?
The advantage of using unarchive here is that I can change the type of value_m without having to update this line of code - the type always follows the member variable's type.
One alternative is to create an alias for the type of value_m and eliminate the decltype(value_m) from the constructor initializer list:
struct foo
{
using value_type = int;
foo(const dictionary_t& archive, const key_type value_key) :
value_m(unarchive<value_type>(archive, value_key))
{ }
value_type value_m;
};
the unarchive<value_type> still follows the type of value_m. A static_assert could be added to ensure the type of value_m is the same as value_type if there is concern of a change to the type of value_m by not changing value_type:
static_assert(std::is_same<decltype(value_m), value_type>::value,
"'value_m' type differs from 'value_type'");
or set the alias based on the type of value_m:
int value_m;
using value_type = decltype(value_m);
If you still consider the constructor initialization list verbose provide a static wrapper function that invokes the unarchive() function:
struct foo
{
using value_type = int;
foo(const dictionary_t& archive, const key_type value_key) :
value_m(unarchive_(archive, value_key))
{ }
static value_type unarchive_(const dictionary_t& d, key_type k)
{
return unarchive<value_type>(d, k);
}
value_type value_m;
};
Having said all that:
value_m(unarchive<decltype(value_m)>(archive, value_key))
is not that verbose and precisely states the intention.
It's a bit hackish, but how about introducing a wrapper class for the archive with a templated conversion operator:
class wrapper {
const dictionary_t& dict_m;
const key_type key_m;
public:
wrapper(const dictionary_t& d, key_type k) :
dict_m(d), key_m(k) {}
template <class T> operator T () const {
return unarchive<T>(dict_m, key_m);
}
};
so you can initialize with:
foo(const dictionary_t& archive, const key_type value_key) :
value_m(wrapper(archive, value_key))
{}
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.
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;