Reputation: 676
So I have a variadic template class that has a method in which arguments for the template are captured in a lambda. Later that lambda is called. Problem is, ints that are either at the front or the back of the pack lose their value.
Here is a simplified example:
#include <iostream>
#include <functional>
void doPrint() {}
template <typename Arg, typename... Args>
void doPrint(Arg arg, Args... args) {
std::cout << arg << std::endl;
doPrint(std::forward<Args>(args)...);
}
template <typename... Args>
void print(Args... args) {
doPrint(std::forward<Args>(args)...);
}
class IntWrapper {
public:
IntWrapper(int val) : val(val) {}
int val;
};
std::ostream& operator<<(std::ostream& out, const IntWrapper& value) {
out << value.val;
return out;
}
template <typename... Args>
class TestClass {
public:
void createWrapper(Args... args) {
wrapper = [&]() -> void {
print(std::forward<Args>(args)...);
};
}
std::function<void()> wrapper;
};
int main(int argc, char *argv[])
{
std::string string = "abc";
std::cout << "Test 1:" << std::endl;
TestClass<int, const IntWrapper&, int> test1;
test1.createWrapper(1, IntWrapper(2), 3);
test1.wrapper();
std::cout << std::endl << "Test 2:" << std::endl;
TestClass<int, const IntWrapper&> test2;
test2.createWrapper(1, IntWrapper(2));
test2.wrapper();
std::cout << std::endl << "Test 3:" << std::endl;
TestClass<const IntWrapper&, int> test3;
test3.createWrapper(IntWrapper(1), 2);
test3.wrapper();
std::cout << std::endl << "Test 4:" << std::endl;
TestClass<const IntWrapper&, int, const IntWrapper&> test4;
test4.createWrapper(IntWrapper(1), 2, IntWrapper(3));
test4.wrapper();
}
This is the output I get:
Test 1:
1
2
3
Test 2:
32764
2
Test 3:
1
32764
Test 4:
1
0
3
As you can see, the wrapped ints always keep their values, but the ints sometimes don't. I know it works if I capture by copy and don't use forward, but I can't do that in my actual use case.
So, why doesn't this work? And is there any way to fix it?
EDIT: Okay, well, so obviously I was being stupid by letting the variables go out of scope in the example. It works with local variables. However, the problem still occurs in my use case, where the variables aren't out of scope. I'll try to transfer the problem to my example and try again.
Upvotes: 3
Views: 171
Reputation: 76
So what's happening is that your references are going out of scope before you use them.
test1.createWrapper(1, IntWrapper(2), 3);
all of those variables you passed go out of scope by the time you get to
test1.wrapper();
if you stored them locally then called, something like
int x = 1, z = 3;
IntWrapper y(2);
test1.createWrapper(x, y, z);
test1.wrapper();
it should work. For your actual code you need to either make sure that the values are still valid from when you call createWrapper to whenever you call wrapper, or you'll need to capture by value in createWrapper.
Edit:
I missed this:
TestClass<int, const IntWrapper&, int> test1;
for test1 you aren't forwarding the int's passed in (x and z in my example above) you're forwarding the copy of x and z because those int's are being passed by value. If you change to
TestClass<int&, const IntWrapper&, int&> test1;
then I think it would work.
Upvotes: 5