templated constructor loses rvalue - c++

I am using a templated constructor in one of my classes
Could anybody tell me why this code compiles with no warnings or errors and yet one line of code (create test with default foo rvalue) just disappears!
#include <iostream>
class Foo
{
int _value;
public:
Foo()
:_value(0)
{
std::cout << __PRETTY_FUNCTION__ << "value[" << _value << "]" << std::endl;
}
Foo(int value)
:_value(value)
{
std::cout << __PRETTY_FUNCTION__ << "value[" << _value << "]" << std::endl;
}
Foo(Foo&& foo) = delete;
Foo(const Foo& foo) = delete;
friend std::ostream& operator << (std::ostream& output, const Foo& foo)
{
output << foo._value;
return output;
}
};
class Test
{
public:
template <typename Type>
Test(Type&& value)
{
std::cout << __PRETTY_FUNCTION__ << " with value[" << value << "]" << std::endl;
}
template <typename Type>
void fn(Type&& value)
{
std::cout << __PRETTY_FUNCTION__ << " with value[" << value << "]" << std::endl;
}
};
int main()
{
std::cout << "//----- test fn with foo rvalue ---------------" << std::endl;
Test test3(3);
test3.fn(Foo());
std::cout << "//----- test with int rvalue ------------------" << std::endl;
Test test4(1+3);
std::cout << "//----- create test with default foo rvalue ---" << std::endl;
Test test5(Foo());
std::cout << "//----- create test with foo rvalue -----------" << std::endl;
Test test7 (Foo(1+6));
std::cout << "//----- create test with moved foo rvalue -----" << std::endl;
Test test8(std::move(Foo()));
return 0;
}
This produces the following result
//----- test fn with foo rvalue ---------------
Test::Test(Type&&) [with Type = int] with value[3]
Foo::Foo()value[0]
void Test::fn(Type&&) [with Type = Foo] with value[0]
//----- test with int rvalue ------------------
Test::Test(Type&&) [with Type = int] with value[4]
//----- create test with default foo rvalue ---
//----- create test with foo rvalue -----------
Foo::Foo(int)value[7]
Test::Test(Type&&) [with Type = Foo] with value[7]
//----- create test with moved foo rvalue -----
Foo::Foo()value[0]
Test::Test(Type&&) [with Type = Foo] with value[0]
I am using g++ (Ubuntu 4.8.2-19ubuntu1) 4.8.2 with std=c++1y
If it helps, I added a line at the end
std::cout << test5;
And the compiler produced a warning
warning: the address of ‘Test test5(Foo (*)())’ will always evaluate as ‘true’ [-Waddress]

Test test5(Foo());
It's function declaration, not object creation. For create object you can use for example one of the following
Test test5((Foo()));
Test test5 = Test(Foo());
Test test5{Foo()};

Related

Missing automatic move of returned local variable

I'm running into an issue where under a specific set of conditions, a local variable being returning by a function is copied instead of moved. So far, it seems like it needs to meet the following:
The returned variable has some usage in the function. I'm assuming otherwise the whole copy/move is ellided.
The returned type is using a perfect forwarding-style constructor. From this answer (Usage of std::forward vs std::move) I learned that this style of constructor has some different deduction rules.
Be compiled in gcc before 8.0. Compiling under clang (and apparently gcc 8.0+, thanks PaulMcKenzie for the comment) produces the results I expect, however I'm not free to change the compiler being used in the larger project.
Here's some minimum reproduction:
#include <iostream>
#include <type_traits>
// Test class to print copies vs. moves.
class Value
{
public:
Value() : x(0) {}
Value(Value&& other) : x(other.x)
{
std::cout << "value move" << std::endl;
}
Value(const Value& other) : x(other.x)
{
std::cout << "value copy" << std::endl;
}
int x;
};
// A container class using a separate lvalue and rvalue conversion constructor.
template<typename T>
class A
{
public:
A(const T& v) : data_(v)
{
std::cout << "lvalue conversion" << std::endl;
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
A(T&& v) : data_(std::move(v))
{
std::cout << "rvalue conversion" << std::endl;
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
T data_;
};
// A container class using a single perfect forwarding constructor.
template<typename T>
class B
{
public:
template <typename U>
B(U&& v) : data_(std::forward<U>(v))
{
std::cout << "template conversion" << std::endl;
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
T data_;
};
// Get a Value rvalue.
Value get_v()
{
Value v;
v.x = 10; // Without this things get ellided.
return v;
}
// Get an A<Value> rvalue.
A<Value> get_a()
{
Value v;
v.x = 10; // Without this things get ellided.
return v;
}
// Get a B<Value> rvalue.
B<Value> get_b()
{
Value v;
v.x = 10; // Without this things get ellided.
return v;
}
int main()
{
Value v = Value();
std::cout << "--------\nA" << std::endl;
std::cout << "lvalue" << std::endl;
A<Value> a0(v);
std::cout << a0.data_.x << std::endl;
std::cout << "rvalue" << std::endl;
A<Value> a1(get_v());
std::cout << a1.data_.x << std::endl;
std::cout << "get_a()" << std::endl;
std::cout << get_a().data_.x << std::endl;
std::cout << "--------\nB" << std::endl;
std::cout << "lvalue" << std::endl;
B<Value> b0(v);
std::cout << b0.data_.x << std::endl;
std::cout << "rvalue" << std::endl;
B<Value> b1(get_v());
std::cout << b1.data_.x << std::endl;
std::cout << "get_b()" << std::endl;
std::cout << get_b().data_.x << std::endl;
return 0;
}
Under gcc this produces:
--------
A
lvalue
value copy
lvalue conversion
A<T>::A(const T&) [with T = Value]
0
rvalue
value move
rvalue conversion
A<T>::A(T&&) [with T = Value]
10
get_a()
value move <---- Works with separate constructors.
rvalue conversion
A<T>::A(T&&) [with T = Value]
10
--------
B
lvalue
value copy
template conversion
B<T>::B(U&&) [with U = Value&; T = Value]
0
rvalue
value move
template conversion
B<T>::B(U&&) [with U = Value; T = Value]
10
get_b()
value copy <---- Not what I expect!
template conversion
B<T>::B(U&&) [with U = Value&; T = Value]
10
For completeness, clang gives:
--------
A
lvalue
value copy
lvalue conversion
A<Value>::A(const T &) [T = Value]
0
rvalue
value move
rvalue conversion
A<Value>::A(T &&) [T = Value]
10
get_a()
value move
rvalue conversion
A<Value>::A(T &&) [T = Value]
10
--------
B
lvalue
value copy
template conversion
B<Value>::B(U &&) [T = Value, U = Value &]
0
rvalue
value move
template conversion
B<Value>::B(U &&) [T = Value, U = Value]
10
get_b()
value move <---- Like this!
template conversion
B<Value>::B(U &&) [T = Value, U = Value]
10
I have two questions:
Is this allowed behavior by gcc?
Is there any way to force move behavior here through changing the implementation of A/B? It seems any time you have a templated function parameter being taken as && it will trigger the special rules for perfect forwarding, so if I try to provide two constructors as in the A example, one taking const U& and one taking U&&, it won't avoid the problem as long as they have other templating in place.

Copy assignment operator for variadic std::tuple

I have a variadic template with a variadic std::tuple as a member.
I declared the default assignment operator.
MSVC complains that the operator is deleted. I can still run the program.
It doesnt give the expected result.
So 3 Questions come to my mind:
Why is the default copy assignment operator not generated even if flagged as default?
Why can I run the programm still with the code posted?
How to declare a assignment operator for std::tuple<T...> template?
#include <string>
#include <iostream>
#include <tuple>
template<typename ...T>
class Variadic
{
public:
explicit Variadic(T... args)
:m_data{ std::move(args)... }
{
}
Variadic& operator=(const Variadic&) = default;
template<typename Type>
Type get_element() const
{
return std::get<Type>(m_data);
}
private:
std::tuple<T...> m_data{};
};
int main()
{
Variadic<int, std::string> tuple1{ 1, "a" };
Variadic<int, std::string> tuple2{ 2, "b" };
std::cout << tuple1.get_element<int>() << '\n'; // 1
std::cout << tuple1.get_element<std::string>() << '\n'; // a
std::cout << tuple2.get_element<int>() << '\n'; // 2
std::cout << tuple2.get_element<std::string>() << '\n'; // b
tuple2 = tuple1; // MSVC2017 gives warning here:
//function "Variadic<T...>::operator=(const Variadic<T...> &) [with T=<int, std::string>]"
//(declared at line 13) cannot be referenced -- it is a deleted function
// still it compiles ???
std::cin.get();
std::cout << tuple1.get_element<int>() << '\n'; // 1
std::cout << tuple1.get_element<std::string>() << '\n'; // a
std::cout << tuple2.get_element<int>() << '\n'; // expect 1 but 2 ?
std::cout << tuple2.get_element<std::string>() << '\n'; // expect b but a ?
}

What causes a move assignment operator to not be called?

I'm working on my own smart pointer and I ran into some weird problems. The move assignment operator was not being called. So I wrote a test class and was able to reproduce the issue. The move assignment operator is not called but a copy assignment occurs (even when there is no copy assignment operator).
This is my test class
#include <utility>
#include <iostream>
struct tag_t {};
constexpr tag_t tag {};
template <typename T>
struct Foo {
Foo() noexcept
: val{} {
std::cout << "Default construct\n";
}
template <typename U>
Foo(tag_t, const U &val) noexcept
: val{val} {
std::cout << "Construct " << val << '\n';
}
~Foo() noexcept {
std::cout << "Destruct " << val << '\n';
}
template <typename U>
Foo(Foo<U> &&other) noexcept
: val{std::exchange(other.val, U{})} {
std::cout << "Move construct " << val << '\n';
}
template <typename U>
Foo &operator=(Foo<U> &&other) noexcept {
std::cout << "Move assign " << other.val << '\n';
val = std::exchange(other.val, U{});
return *this;
}
T val;
};
These are the tests
int main() {
{
Foo<int> num;
std::cout << "Value " << num.val << '\n';
num = {tag, 5};
std::cout << "Value " << num.val << '\n';
}
std::cout << '\n';
{
Foo<int> num;
std::cout << "Value " << num.val << '\n';
num = Foo<int>{tag, 5};
std::cout << "Value " << num.val << '\n';
}
return 0;
}
After running the tests, I get these results
Default construct
Value 0
Construct 5
Destruct 5
Value 5
Destruct 5
Default construct
Value 0
Construct 5
Move assign 5
Destruct 0
Value 5
Destruct 5
What baffles me is the output of the first test. The move assignment operator is not called but a copy assignment takes place. This results in 5 being destroyed twice. Not ideal when you're trying to make a smart pointer!
I'm compiling with Apple Clang with optimizations disabled. Can someone explain my observations? Also, how do I ensure that the move assignment operator is called in the first test?
template <typename U>
Foo &operator=(Foo<U> &&other) noexcept;
this cannot be called by ={ }.
Instead, Foo& operator=(Foo&&)noexcept is called.
Template methods are never special member functions. Explicitly default, delete or implement them.

Perfect forwarding, why does the destructor get called twice?

I'm trying to make a function that mimics Python's with statement but I've run into some interesting behavior that I don't quite understand.
With the following program:
#include <iostream>
struct foo {
foo() { std::cout << "foo()" << std::endl; }
~foo() { std::cout << "~foo()" << std::endl; }
};
auto make_foo() -> foo {
return {};
}
template <typename T, typename F>
auto with(T&& t, F&& fn) -> void {
fn(std::forward<T>(t));
}
auto main() -> int {
std::cout << "before" << std::endl;
with(make_foo(), [](auto f) {
std::cout << "during" << std::endl;
});
std::cout << "after" << std::endl;
}
When compiled under with the clang provided by Xcode 6.3 and -std=c++14 and run I get the following output:
before
foo()
during
~foo()
~foo()
after
Does anybody know why I am getting two ~foo()'s in my output?
Here are the two objects:
with(make_foo(), [](auto f) {
1^^^^^^^^^ 2^^^^^^
There is the object returned by make_foo(), and the function argument f.
If you pass by reference (change to auto&& f) then you will only see evidence of one object.
There's no creation message because this is created by copy/move construction and you do not have any output in those constructors.
Note that there may be more objects inside make_foo() but your compiler is doing copy elision.
Your destructor calls don't appear to be matched with constructor calls simply because you aren't tracing copy/move constructors. If we add the tracing like so:
struct foo {
foo() { std::cout << "foo()" << std::endl; }
~foo() { std::cout << "~foo()" << std::endl; }
foo(const foo&) { std::cout << "foo(const foo&)" << std::endl; }
foo(foo&&) { std::cout << "foo(foo&&)" << std::endl; }
};
our output is now:
before
foo()
foo(foo&&)
during
~foo()
~foo()
after
The reason for the move-construction is that your lambda takes its parameter by value:
[](auto f) {
// ^^^^^^
std::cout << "during" << std::endl;
}
If you don't want the copy, take by reference-to-const, or maybe even forwarding reference.
This works for me by accepting an r-reference in the lambda function parameter to prevent a copy being made:
#include <iostream>
struct foo {
foo() { std::cout << "foo()" << std::endl; }
~foo() { std::cout << "~foo()" << std::endl; }
};
auto make_foo() -> foo {
return {};
}
template <typename T, typename F>
auto with(T&& t, F&& fn) -> void {
fn(std::forward<T>(t));
}
auto main() -> int {
std::cout << "before" << std::endl;
with(make_foo(), [](auto&&) { // r-reference!
std::cout << "during" << std::endl;
});
std::cout << "after" << std::endl;
}
New Improved Output:
before
foo()
during
~foo()
after

Template function is used instead of typed

I have this code:
#include <iostream>
#include <string>
class Foo {
public:
Foo(){};
template<typename T>
Foo (T&) {
std::cout << "template" << std::endl;
}
Foo(Foo&) {
std::cout << "copy" << std::endl;
}
Foo(const Foo&) {
std::cout << "copy2" << std::endl;
}
};
int main(){
Foo f;
Foo f2 (f);
}
It prints "copy", which is correct.
However if I remove the Foo(Foo&):
class Foo {
public:
Foo(){};
template<typename T>
Foo (T&) {
std::cout << "template" << std::endl;
}
//Foo(Foo&) {
// std::cout << "copy" << std::endl;
//}
Foo(const Foo&) {
std::cout << "copy2" << std::endl;
}
};
int main(){
Foo f;
Foo f2 (f);
}
it prints "template". I expected it to print "copy2" because it is typed parameters. Why is it using template instead?
In order to perform overload resolution, the compiler needs to evaluate the constructor template using template argument deduction. So it creates a constructor like this:
template<>
Foo<Foo>::Foo(Foo&);
This constructor takes an lvalue reference to non-const, as oppose to the non-template constructor. This is preferred because an identity conversion (i.e no conversion) is preferred over a qualification conversion.
Foo f;
f is not a const object of Foo. So its type match to following function and it will print template.
template<typename T>
Foo (T&) {
std::cout << "template" << std::endl;
}
If you define f as follows, it will print as you expected.
const Foo f;
For the same reason that your first code sample doesn't print copy2. The f you are referencing is not const, so the compiler first tries non-const references. Overload matching matches template<typename T=Foo> Foo(T&) giving an exact match while Foo(const Foo&) requires a cv-adjustment of the parameter being passed.
Try the following:
#include <iostream>
class Foo {
public:
Foo(){};
template<typename T>
Foo (T&) {
std::cout << "template" << std::endl;
}
//Foo(Foo&) {
// std::cout << "copy" << std::endl;
//}
Foo(const Foo&) {
std::cout << "copy2" << std::endl;
}
};
int main(){
const Foo f;
Foo f2 (f);
}
http://ideone.com/zOf1qX