I have the following function declaration:
void get_data(struct myStruct* const value, const void * const data);
I have another function that I want to add a std::function as a parameter:
// I think the std::function definition is where my problem is
template<typename MyType>
void process(bool test, std::function<void(MyType* const, const void* const)>& callb) { ... }
However I can't quite figure out how to call it, or rather if my definition above is correct:
bool test1 = true;
process<myStruct>(test1, get_data);
Compiler Error:
error: prototype for ‘void process(bool, std::function<void(MyType* const, const void* const)>&)’ does not match any in class ‘MyClass’
void process(bool test,
error: candidate is: template<class MyType> void process(bool, std::function<void(MyType*, const void*)>&)
void process(bool test,
... goes on
Any ideas?
You basically just have to remove the reference from the function object:
void process(bool test, std::function<void(MyType* const, const void* const)> callb);
While a reference to a std::function object cannot be related to/converted from the underlying function pointer type, the non-referenced object can be implicitely converted to the function pointer type thanks to its constructor.
Edit: Passing std::function as const reference gives no performance benefit, but may actually include some penalty in some corner cases, see Should I pass an std::function by const-reference?
When passing get_value to callb, the compiler has to construct a temporary std::function object for callb. But callb is declared as a non-const reference, which cannot be bound to a temporary object. So make callb be a const reference instead, which can be bound to a temporary:
template<typename MyType>
void process(bool test, const std::function<void(MyType* const, const void* const)>& callb) { ... }
Live Demo
Related
I have a class template which has a member variable std::vector<T> buffer and a member function setData, among others.
template <class T>
void ArrayT<T>::setData(const std::vector<T> * & data_ptr) {
buffer.assign(data_ptr->begin(), data_ptr->end());
}
I want setData to perform a deep copy of the data passed as its argument. As you can see, the argument is of type * &, which if I understand well is a pointer parameter passed by reference.
When I write:
vector<float> vec(3, 1.0);
vector<float>* vec2 = &vec;
A obj();
obj.setData(vec2);
I get an error:
E0434 a reference of type "const std::vector<float, std::allocator<float>> *&" (not const-qualified) cannot be initialized with a value of type "std::vector<float, std::allocator<float>> *"
Could someone please explain how to fix this?
The input parameter of setData() is a reference to a non-const pointer to a const std::vector. But the std::vector object you are pointing at is not const.
There is no reason to use a pointer at all in this situation. Change setData() to take the std::vector by const reference instead:
template <class T>
void ArrayT<T>::setData(const std::vector<T> &data)
{
buffer.assign(data.begin(), data.end());
// or simply: buffer = data;
}
vector<float> vec(3, 1.0);
Array<float> obj;
obj.setData(vec);
That being said, if you want to keep the pointer, you need to drop the const:
template <class T>
void ArrayT<T>::setData(std::vector<T> * data_ptr)
{
buffer.assign(data_ptr->begin(), data_ptr->end());
// or simply: buffer = *data_ptr;
}
Or at least move the const after the * to make the pointer itself const rather than the object it is pointing at:
template <class T>
void ArrayT<T>::setData(std::vector<T> * const data_ptr)
{
buffer.assign(data_ptr->begin(), data_ptr->end());
// or simply: buffer = *data_ptr;
}
Either way, notice I removed the reference. There is no good reason to pass a pointer by reference when you are not modifying what the pointer is pointing at. Pass the pointer by value instead.
Given the following code:
template <class Func>
void f(Func func , int* param){
func(/* how can I send "param" as const "int*" */);
}
How can I do it so that if f don't get the variable as const - so we will get error ?
If you want to make sure that f accepts a pointer to const-qualified int you can cast function argument appropriately:
f(static_cast<int const *>(param));
Alternatively, if you want to make sure that f accepts a reference to const-qualified pointer you can add const qualifier to function argument:
void f(Func f , int * const param)
Here is code example:
#include <string>
#include <functional>
struct Foo {};
typedef bool func_type(Foo *&, const std::string&);
typedef std::function<bool(Foo*&, const std::string&)> FunctionalType;
bool f(Foo *, const std::string&)
{
}
int main()
{
#if 1
func_type *func;
func = f;
#else
FunctionalType f2;
f2 = f;
#endif
}
As you see, I have declared function type with "reference to pointer" as the first argument Foo *&, and I expect that function with just "pointer" as the first argument Foo * cannot be assigned to a variable of this type.
The #if 1 region fails to compile (as I expect); however, the alternative did not emit any errors:
FunctionalType f2;
f2 = f;
Why does it compile without error (with at least gcc 5.2 and clang 3.7)?
How it can be fixed, so that std::function<Params> does not accept f for conversion?
std::function<R(Ts...)> is defined as a type whose objects can represent any function that can be called with arguments Ts... and whose return value is convertible to R.
Since your function f can be called with an lvalue of type T* as the first argument (which is the requirement that your Foo *& imposes), it is a valid function to be stored in your std::function.
There is no way to suppress this behavior that I know of.
std::function is a type-erasing container of invokable things.
It will store an instance of any C++ type that can be copied, destroyed and invoked with a "compatible" signature.
In this case, the signature is bool(Foo*&, const std::string&).
The core idea is that when Args... in the R(Args...) part of the std::function type is Foo*&, const std::string&, those arguments can be passed to a function expecting Foo* and const std::string&.
std::function works based on compatibility, not exact matching of signatures.
If you really, really need to ban things that do not take references:
template<class T>
struct reference_only {
T& t;
operator T&(){ return t; }
operator T()=delete;
reference_only(T& tin):t(tin){}
};
then use:
typedef std::function<void(reference_only<Foo*>)> FunctionalType;
which doesn't like being converted to a value-type, but accepts being converted to a reference type (of type Foo*& in this case).
Live example compiling, live example not compiling.
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;