As an example, say I am writing a thin wrapper for a vector's push_back method.
class Foo
{
public:
void myPushBack(Bar b); // Line in question
private:
std::vector<Bar> vec;
}
void Foo::MyPushBack(bar b)
{
vec.push_back(bar);
}
main()
{
Foo f();
f.myPushBack();
}
My question is what is the correct signature for the function myPushBack? Then my next question is what would be the correct signature for the function myPushBack if vec was of type std::vector<weak_ptr<Bar>>?
Assuming that you are using C++11, you should use the perfect forwarding idiom:
template<typename T> void Foo::MyPushBack(T &&b)
{
vec.push_back(std::forward<T>(b));
}
Since it is a template, it does not matter the actual type of the vector. It will even take into account implicit conversions, such as const char* to std::string.
Related
I'm defining a class like this:
class foo {
public:
// I define const and non-const versions of the 'visit' function
// nb. the lambda is passed by reference
virtual void visitWith(std::function<void(foo&)>&);
virtual void visitWith(std::function<void(const foo&)>&) const;
};
foo can have children so the idea is to visit a foo and all it's children recursively.
When I try to use it, eg. like this:
foo f;
f.visitWith([&](const foo&) {
// Do something here
});
I get compiler errors. The compiler can't figure out what to do.
I can make it work it by adding a typecast like this:
foo f;
f.visitWith( (std::function<void(const foo&)>) [&](const foo&) {
// Do something here
});
But that's horrible.
How can I get it to work neatly?
Edit:
This may be a problem with Visual C++, it refuses to compile the code given here:
https://ideone.com/n9bySW
The VC++ output when I try to compile it is:
Edit2: Nope, Visual C++ is correct, the code is ambiguous. See my solution below...
A lambda is a compiler-generated type, it is not an instance of std::function, but it is assignable to one.
Your visitWith() method takes a std::function by non-const reference, which means it requires a pre-existing std::function object, eg:
std::function<void(const foo&)> func = [&](const foo&) {
// Do something here
};
foo f;
f.visitWith(func);
Passing a lambda directly to visitWith() would require the compiler to create a temporary std::function object, but a non-const reference cannot bind to a temporary object. That is why your original code fails to compile.
For what you are attempting, you will have to pass the std::function either by value or by const-reference instead:
class foo {
public:
void visitWith(std::function<void(foo&)>);
void visitWith(std::function<void(const foo&)>) const;
};
Live Demo
class foo {
public:
void visitWith(const std::function<void(foo&)> &);
void visitWith(const std::function<void(const foo&)> &) const;
};
Live Demo
I reported this "bug" to Microsoft and got a reply, here:
https://developercommunity.visualstudio.com/content/problem/1201858/c-stdfunction-overloading-fails.html
Short version: Visual C++ is handling it correctly, ideone is wrong.
In the end I solved it by adding a third overload to foo which can add const-ness to an object, like this:
class foo {
public:
// Use typedefs so that all the code that comes after these two functions is neater
typedef std::function<void(Branch&)>visitor;
typedef std::function<void(const Branch&)>const_visitor;
virtual void visitWith(const visitor&);
virtual void visitWith(const const_visitor&) const;
// This is to thunk the third case that can happen when you start
// a const visit from a non-const foo.
void visitWith(const const_visitor& v) {
static_cast<const foo*>(this)->visitWith(v); // Add const-ness
}
};
Now the code works, eg.:
foo f;
f.visitWith([](const foo& f) {
std::cout << "visited a const foo!" << std::endl;
});
Consider std::vector<T> for some type T. I receive a pointer to such a type into a function, and also an instance of T; t say.
My function looks like this:
void bar(std::vector<T>* foo, const T& t)
{
foo->clear();
foo->push_back(t);
}
Is there a way I write the function body in one statement? *foo = t; does not work due to an appropriate assignment operator not existing. I was also thinking of using
foo->assign(&t, &t + 1);
but that seems naughty.
I'm using C++11.
Sure, you can reassign:
*foo = {t};
Is there a reason you can't just use std::vector<>'s other assign() member function?
void bar(std::vector<T>* foo, const T& t)
{
foo->assign(1, t);
}
I have to store arguments (parameter pack), and pass the arguments to another function.
As a result, I cannot use lambda. And a good choice is std::bind.
But for this code
struct A{};
void test(A &&){}
int main()
{
A a;
test(move(a)); //work
bind(test,a)(); //compile fail; copy a to std::bind, pass a to test
}
According to standard, all variables stored in std::bind will be pass as lvalue to function. (The C++ standard doesn't say that, by I think that is what it means.)
And that means I cannot use a function (has rvalue reference in parameter) with std::bind.
One solution is to change test(A &&) to test(A &), but this only works for your project (and make it strange while you not only need to call test by std::thread but also need to call test by plain sequential call).
So, is there any ways to solve this problem?
You can create wrapper which will be convertible to the rvalue reference (like reference_wrapper/l-value references) and use it with bind:
It cal look like that:
#include <iostream>
#include <functional>
struct A{};
void test(A &&){ std::cout << "Works!\n"; }
template <typename T>
struct rvalue_holder
{
T value;
explicit rvalue_holder(T&& arg): value(arg) {}
operator T&&()
{
return std::move(value);
}
};
template <typename T>
rvalue_holder<T> rval(T && val)
{
return rvalue_holder<T>(std::move(val));
}
int main()
{
A a;
test(std::move(a)); //work
auto foo = std::bind(test, rval(std::move(a))); //works
foo();
}
http://coliru.stacked-crooked.com/a/56220bc89a32c860
Note: both rvalue_holder and especially rval need further work to ensure efficiency, robustness and desired behavior in all cases.
Imagine you have a number of overloaded methods that (before C++11) looked like this:
class MyClass {
public:
void f(const MyBigType& a, int id);
void f(const MyBigType& a, string name);
void f(const MyBigType& a, int b, int c, int d);
// ...
};
This function makes a copy of a (MyBigType), so I want to add an optimization by providing a version of f that moves a instead of copying it.
My problem is that now the number of f overloads will duplicate:
class MyClass {
public:
void f(const MyBigType& a, int id);
void f(const MyBigType& a, string name);
void f(const MyBigType& a, int b, int c, int d);
// ...
void f(MyBigType&& a, int id);
void f(MyBigType&& a, string name);
void f(MyBigType&& a, int b, int c, int d);
// ...
};
If I had more parameters that could be moved, it would be unpractical to provide all the overloads.
Has anyone dealt with this issue? Is there a good solution/pattern to solve this problem?
Thanks!
Herb Sutter talks about something similar in a cppcon talk
This can be done but probably shouldn't. You can get the effect out using universal references and templates, but you want to constrain the type to MyBigType and things that are implicitly convertible to MyBigType. With some tmp tricks, you can do this:
class MyClass {
public:
template <typename T>
typename std::enable_if<std::is_convertible<T, MyBigType>::value, void>::type
f(T&& a, int id);
};
The only template parameter will match against the actual type of the parameter, the enable_if return type disallows incompatible types. I'll take it apart piece by piece
std::is_convertible<T, MyBigType>::value
This compile time expression will evaluate to true if T can be converted implicitly to a MyBigType. For example, if MyBigType were a std::string and T were a char* the expression would be true, but if T were an int it would be false.
typename std::enable_if<..., void>::type // where the ... is the above
this expression will result in void in the case that the is_convertible expression is true. When it's false, the expression will be malformed, so the template will be thrown out.
Inside the body of the function you'll need to use perfect forwarding, if you are planning on copy assigning or move assigning, the body would be something like
{
this->a_ = std::forward<T>(a);
}
Here's a coliru live example with a using MyBigType = std::string. As Herb says, this function can't be virtual and must be implemented in the header. The error messages you get from calling with a wrong type will be pretty rough compared to the non-templated overloads.
Thanks to Barry's comment for this suggestion, to reduce repetition, it's probably a good idea to create a template alias for the SFINAE mechanism. If you declare in your class
template <typename T>
using EnableIfIsMyBigType = typename std::enable_if<std::is_convertible<T, MyBigType>::value, void>::type;
then you could reduce the declarations to
template <typename T>
EnableIfIsMyBigType<T>
f(T&& a, int id);
However, this assumes all of your overloads have a void return type. If the return type differs you could use a two-argument alias instead
template <typename T, typename R>
using EnableIfIsMyBigType = typename std::enable_if<std::is_convertible<T, MyBigType>::value,R>::type;
Then declare with the return type specified
template <typename T>
EnableIfIsMyBigType<T, void> // void is the return type
f(T&& a, int id);
The slightly slower option is to take the argument by value. If you do
class MyClass {
public:
void f(MyBigType a, int id) {
this->a_ = std::move(a); // move assignment
}
};
In the case where f is passed an lvalue, it will copy construct a from its argument, then move assign it into this->a_. In the case that f is passed an rvalue, it will move construct a from the argument and then move assign. A live example of this behavior is here. Note that I use -fno-elide-constructors, without that flag, the rvalue cases elides the move construction and only the move assignment takes place.
If the object is expensive to move (std::array for example) this approach will be noticeably slower than the super-optimized first version. Also, consider watching this part of Herb's talk that Chris Drew links to in the comments to understand when it could be slower than using references. If you have a copy of Effective Modern C++ by Scott Meyers, he discusses the ups and downs in item 41.
You may do something like the following.
class MyClass {
public:
void f(MyBigType a, int id) { this->a = std::move(a); /*...*/ }
void f(MyBigType a, string name);
void f(MyBigType a, int b, int c, int d);
// ...
};
You just have an extra move (which may be optimized).
My first thought is that you should change the parameters to pass by value. This covers the existing need to copy, except the copy happens at the call point rather than explicitly in the function. It also allows the parameters to be created by move construction in a move-able context (either unnamed temporaries or by using std::move).
Why you would do that
These extra overloads only make sense, if modifying the function paramers in the implementation of the function really gives you a signigicant performance gain (or some kind of guarantee). This is hardly ever the case except for the case of constructors or assignment operators. Therefore, I would advise you to rethink, whether putting these overloads there is really necessary.
If the implementations are almost identical...
From my experience this modification is simply passing the parameter to another function wrapped in std::move() and the rest of the function is identical to the const & version. In that case you might turn your function into a template of this kind:
template <typename T> void f(T && a, int id);
Then in the function implementation you just replace the std::move(a) operation with std::forward<T>(a) and it should work. You can constrain the parameter type T with std::enable_if, if you like.
In the const ref case: Don't create a temporary, just to to modify it
If in the case of constant references you create a copy of your parameter and then continue the same way the move version works, then you may as well just pass the parameter by value and use the same implementation you used for the move version.
void f( MyBigData a, int id );
This will usually give you the same performance in both cases and you only need one overload and implementation. Lots of plusses!
Significantly different implementations
In case the two implementations differ significantly, there is no generic solution as far as I know. And I believe there can be none. This is also the only case, where doing this really makes sense, if profiling the performance shows you adequate improvements.
You might introduce a mutable object:
#include <memory>
#include <type_traits>
// Mutable
// =======
template <typename T>
class Mutable
{
public:
Mutable(const T& value) : m_ptr(new(m_storage) T(value)) {}
Mutable(T& value) : m_ptr(&value) {}
Mutable(T&& value) : m_ptr(new(m_storage) T(std::move(value))) {}
~Mutable() {
auto storage = reinterpret_cast<T*>(m_storage);
if(m_ptr == storage)
m_ptr->~T();
}
Mutable(const Mutable&) = delete;
Mutable& operator = (const Mutable&) = delete;
const T* operator -> () const { return m_ptr; }
T* operator -> () { return m_ptr; }
const T& operator * () const { return *m_ptr; }
T& operator * () { return *m_ptr; }
private:
T* m_ptr;
char m_storage[sizeof(T)];
};
// Usage
// =====
#include <iostream>
struct X
{
int value = 0;
X() { std::cout << "default\n"; }
X(const X&) { std::cout << "copy\n"; }
X(X&&) { std::cout << "move\n"; }
X& operator = (const X&) { std::cout << "assign copy\n"; return *this; }
X& operator = (X&&) { std::cout << "assign move\n"; return *this; }
~X() { std::cout << "destruct " << value << "\n"; }
};
X make_x() { return X(); }
void fn(Mutable<X>&& x) {
x->value = 1;
}
int main()
{
const X x0;
std::cout << "0:\n";
fn(x0);
std::cout << "1:\n";
X x1;
fn(x1);
std::cout << "2:\n";
fn(make_x());
std::cout << "End\n";
}
This is the critical part of the question:
This function makes a copy of a (MyBigType),
Unfortunately, it is a little ambiguous. We would like to know what is the ultimate target of the data in the parameter. Is it:
1) to be assigned to an object that existing before f was called?
2) or instead, stored in a local variable:
i.e:
void f(??? a, int id) {
this->x = ??? a ???;
...
}
or
void f(??? a, int id) {
MyBigType a_copy = ??? a ???;
...
}
Sometimes, the first version (the assignment) can be done without any copies or moves. If this->x is already long string, and if a is short, then it can efficiently reuse the existing capacity. No copy-construction, and no moves. In short, sometimes assignment can be faster because we can skip the copy contruction.
Anyway, here goes:
template<typename T>
void f(T&& a, int id) {
this->x = std::forward<T>(a); // is assigning
MyBigType local = std::forward<T>(a); // if move/copy constructing
}
If the move version will provide any optimization then the implementation of the move overloaded function and the copy one must be really different. I don't see a way to get around this without providing implementations for both.
When writing a class to act as a wrapper around a heap-allocated object, I encountered a problem with implicit type conversion that can be reduced to this simple example.
In the code below the wrapper class manages a heap-allocated object and implicitly converts to a reference to that object. This allows the wrapper object to be passed as the argument to the function write(...) since implicit conversion takes place.
The compiler fails, however, when trying to resolve the call to operator<<(...), unless an explicit cast is made (checked with MSVC8.0, Intel 9.1 and gcc 4.2.1 compilers).
So, (1) why does the implicit conversion fail in this case? (2) could it be related to argument-dependent lookup? and (3) is there anything that can be done to make this work without the explicit cast?
#include <fstream>
template <typename T>
class wrapper
{
T* t;
public:
explicit wrapper(T * const p) : t(p) { }
~wrapper() { delete t; }
operator T & () const { return *t; }
};
void write(std::ostream& os)
{
os << "(1) Hello, world!\n";
}
int main()
{
wrapper<std::ostream> file(new std::ofstream("test.txt"));
write(file);
static_cast<std::ostream&>( file ) << "(2) Hello, world!\n";
// file << "(3) This line doesn't compile!\n";
}
It fails because you're trying to resolve an operator of your wrapper<T> class that doesn't exist. If you want it to work without the cast, you could put together something like this:
template<typename X> wrapper<T> &operator <<(X ¶m) const {
return t << param;
}
Unfortunately I don't know of a way to resolve the return type at compile time. Fortunately in most cases it's the same type as the object, including in this case with ostream.
EDIT: Modified code by suggestion from dash-tom-bang. Changed return type to wrapper<T> &.
The compiler doesn't have enough context to determine that operator& will make a valid conversion. So, yes, I think this is related to argument-dependent lookup: The compiler is looking for an operator<< that can accept a non-const wrapper<std::ostream> as its first parameter.
I think the problem has to do with maintaining some compile-time constraints. In your example, the compiler first would have to find all the possible operator<<. Then, for each of them, it should try if your object could be automatically converted (directly or indirectly) to any of the types that each operator<< are able to accept.
This test can be really complex, and I think this is restricted to provide a reasonable compiling time.
After some testing, an even simpler example identifies the source of the problem. The compiler cannot deduce the template argument T in f2(const bar<T>&) below from implicit conversion of wrapper<bar<int> > to bar<int>&.
template <typename T>
class wrapper
{
T* t;
public:
explicit wrapper(T * const p) : t(p) { }
~wrapper() { delete t; }
operator T & () const { return *t; }
};
class foo { };
template <typename T> class bar { };
void f1(const foo& s) { }
template <typename T> void f2(const bar<T>& s) { }
void f3(const bar<int>& s) { }
int main()
{
wrapper<foo> s1(new foo());
f1(s1);
wrapper<bar<int> > s2(new bar<int>());
//f2(s2); // FAILS
f2<int>(s2); // OK
f3(s2);
}
In the original example, std::ostream is actually a typedef for the templated class std::basic_ostream<..>, and the same situation applies when calling the templated function operator<<.
Check the signature of the insertion operator... I think they take non-const ostream reference?
Confirmed with C++03 standard, signature of the char* output operator is:
template<class charT, class traits>
basic_ostream<charT,traits>& operator<<(basic_ostream<charT,traits>&, const charT*);
which does indeed take a non-const reference. So your conversion operator does not match.
As noted in the comment: this is irrelevant.
There are various limits in the Standard about conversions applied... maybe this would need to implicit conversions (your operator, and cast to base type) when at most one should be applied.