I'm working out an "Any" class by myself. As following code shown, I have two questions.
#include <assert.h>
#include <iostream>
#include <typeinfo>
class Test{};
class Any {
public:
template<typename DataType>
explicit Any(DataType&& in) {
Test t;
std::cout
<< typeid(t).name() << " "
<< typeid(in).name() << " "
<< typeid(Test()).name();
std::cout << " move";
}
template<typename DataType>
explicit Any(const DataType& in) {
Test t;
std::cout
<< typeid(t).name() << " "
<< typeid(in).name() << " "
<< typeid(Test()).name();
std::cout << " copy";
}
};
int main()
{
Test t;
Any a(t);
}
Compilation command is
g++ main.cpp -std=c++11
The output is
4Test 4Test F4TestvE move
Why does c++ choose move construct rather than copy construct? "t" is an instance of Test which it's not a rvalue.
Why typeid(in) and typeid(Test()) is not same? They are both rvalue.
Thanks a lot.
Why does c++ choose move construct rather than copy construct? "t" is an instance of Test which it's not a rvalue.
The 1st constructor overload takes forwarding reference and could accept both lvalues and rvalues. (Hence it's not move constructor.) For Any a(t); it's an exact match, while for the 2nd overload t needs to be converted to const.
Why typeid(in) and typeid(Test()) is not same? They are both rvalue.
Test() is a function type, which returns Test and takes nothing, thus typeid(Test()) gives different result.
The reason your move variant is invoked is because its signature doesn't actually require an rvalue reference to be passed.
If U = T const& then U&& will be a T const&, not a T&&. In your case DataType binds to Test&, not, as you likely expected, to Test. The && therefore collapses and does nothing. You can see this in action if you add a static_assert(std::is_same_v<DataType, Test&>); which will pass.
You function will be called if you replace the template with a fixed Type, i.e. Test&& instead of DataType&&.
Have a look at reference, section Reference collapsing.
Related
I'm trying to write a wrapper around shared_ptr that can implicitly dereference to underlying type. The code is as below:
#include <memory>
template<typename T>
class PtrWrapper {
public:
PtrWrapper(std::shared_ptr<T> ptr) : ptr_(ptr) {}
operator T& () {
return *ptr_;
}
T& ref() {
return *ptr_;
}
private:
std::shared_ptr<T> ptr_;
};
Looks like that there's nothing wrong with it. I tried a few methods to use the wrapper:
#include <iostream>
class Nothing {
public:
Nothing() {
std::cout << "Construct " << this << std::endl;
}
Nothing(Nothing const& parent) {
std::cout << "Copy " << &parent << " " << this << std::endl;
}
Nothing(Nothing && parent) {
std::cout << "Move " << &parent << " " << this << std::endl;
}
~Nothing() {
std::cout << "Destruct " << this << std::endl;
}
};
int main() {
PtrWrapper<Nothing> wrapper{std::make_shared<Nothing>()};
// #1: OK
Nothing & by_assignment = wrapper;
// #2: OK
Nothing & by_operator{wrapper.operator Nothing &()};
// #3: OK
Nothing & by_function{wrapper.ref()};
// #4: OK
Nothing & by_initialization(wrapper);
// #5: Compile error: non-const lvalue reference to type 'Nothing' cannot bind to an initializer list temporary
// Nothing & by_initialization_2{wrapper};
// #6: The `Nothing` class is copied, which is not expected
Nothing const& by_initialization_3{wrapper};
return 0;
}
The wrapper class works well with assignment and parentheses initialization.
The weird thing is, when I'm trying to initialize Nothing& with initializer list (#5 and #6 in the code above), the value is copied and I must use a const reference to it. However, when I explicitly call the conversion operator like wrapper.operator Nothing &() (#2 in code above), I got the correct reference to the original object constructed at the first line.
I've read cppreference and found that initializer list is a copy-initialized temporary list, but it doesn't make sense why the code works when operator Nothing &() is explicitly called.
Anyone can help me with figuring out what is happening here? Thanks very much!
You are actually doing reference initialization here:
Nothing & by_initialization_2{wrapper};
The rules say that since the initializer is not the same type as the reference being bound, user-defined conversion operators are considered, which is fine, since you have the appropriate conversion operator.
However, if the l-value returned by the conversion function is passed through a brace-init list, then a temporary is materialized. Since you can't bind a non-const reference to a temporary, the initialization fails.
I have made simple test to understand better move semantics. Output result was unexpected for me. Bellow is my test function:
template<class T>
void test(T&& v)
{
v++;
}
void main()
{
int v = 1;
test(std::move(v));
std::cout << "Output:" << v << std::endl;
}
My expectation was:
Output: 1
But real result was:
Output: 2
I thought as follows - I use std::move(v), as result I have conversion to "rvalue" and test function will work with temporal variable. Therefore the result should be Output: 1.
What is wrong in my conclusions?
std::move never constructs a temporary. It is a conversion to an rvalue-reference.
This reference is bound to main::v, then test::v parameter is initialized with it, and thus also bound to main::v.
If you want to create a temporary, use a cast:
test(int{v});
To help you understand what type your template deduced, you can use the macro __PRETTY_FUNCTION__
template<class T>
void test(T&& v)
{
std::cout<< __PRETTY_FUNCTION__ << std::endl;
v++;
}
Also this guys explains a lot of things about template deduction (your case at 35')
https://www.youtube.com/watch?v=vwrXHznaYLA
I have read many posts about variadic templates and std::bind but I think I am still not understanding how they work together. I think my concepts are a little hazy when it comes to using variadic templates, what std::bind is used for and how they all tie together.
In the following code my lambda uses the dot operator with objects of type TestClass but even when I pass in objects of type std::ref they still work. How is this exactly? How does the implicit conversion happen?
#include <iostream>
using std::cout;
using std::endl;
#include <functional>
#include <utility>
using std::forward;
class TestClass {
public:
TestClass(const TestClass& other) {
this->integer = other.integer;
cout << "Copy constructed" << endl;
}
TestClass() : integer(0) {
cout << "Default constructed" << endl;
}
TestClass(TestClass&& other) {
cout << "Move constructed" << endl;
this->integer = other.integer;
}
int integer;
};
template <typename FunctionType, typename ...Args>
void my_function(FunctionType function, Args&&... args) {
cout << "in function" << endl;
auto bound_function = std::bind(function, args...);
bound_function();
}
int main() {
auto my_lambda = [](const auto& one, const auto& two) {
cout << one.integer << two.integer << endl;
};
TestClass test1;
TestClass test2;
my_function(my_lambda, std::ref(test1), std::ref(test2));
return 0;
}
More specifically, I pass in two instances of a reference_wrapper with the two TestClass objects test1 and test2, but when I pass them to the lambda the . operator works magically. I would expect that you have use the ::get() function in the reference_wrapper to make this work but the call to the .integer data member works..
The reference unwrapping is performed by the result of std::bind():
If the argument is of type std::reference_wrapper<T> (for example, std::ref or std::cref was used in the initial call to bind), then the reference T& stored in the bound argument is passed to the invocable object.
Corresponding standardese can be found in N4140 draft, [func.bind.bind]/10.
It is important to note that with std::bind;
The arguments to bind are copied or moved, and are never passed by reference unless wrapped in std::ref or std::cref.
The "passed by reference" above is achieved because std::ref provides a result of std::reference_wrapper that is a value type that "wraps" the reference provided.
std::reference_wrapper is a class template that wraps a reference in a copyable, assignable object. It is frequently used as a mechanism to store references inside standard containers (like std::vector) which cannot normally hold references.
By way of an example of what bind's unwrapping of the reference does (without the bind);
#include <iostream>
#include <utility>
#include <functional>
int main()
{
using namespace std;
int a = 1;
auto b = std::ref(a);
int& c = b;
cout << a << " " << b << " " << c << " " << endl; // prints 1 1 1
c = 2;
cout << a << " " << b << " " << c << " " << endl; // prints 2 2 2
}
Demo code.
Reading Meyers new book I found something very similar to this:
// compile with
// g++-4.8 --std=c++11 -Wall main3.cc && ./a.out
#include <iostream>
#include <vector>
class Widget
{
public:
using DType = std::vector<int>;
DType& data() & // lvalue
{
std::cout << "data (lvalue) : " << &data_[0] << std::endl;
return data_;
};
DType data() && // rvalue
{
std::cout << "data (rvalue) : " << &data_[0] << std::endl;
return std::move(data_);
};
// Please Note
// int parameter is here to make the overloading possible
// in a single class
DType&& data(int) &&
{
std::cout << "data (rvalue ref): " << &data_[0] << std::endl;
return std::move(data_);
};
private:
DType data_ { 0 };
};
Widget getWidget() { return Widget(); }
int main(int argc, char *argv[])
{
Widget w1;
std::vector<int> d1 = w1.data();
std::cout << "d1 copied : " << &d1[0] << std::endl;
std::vector<int> d2 = getWidget().data();
std::cout << "d2 moved : " << &d2[0] << std::endl;
std::vector<int> d3 = getWidget().data(0);
std::cout << "d3 moved : " << &d3[0] << std::endl;
return 0;
}
My point is very simple:
On my box as I would expect I have these results
data (lvalue) : 0x8e28008
d1 copied : 0x8e28018
data (rvalue) : 0x8e28028
d2 moved : 0x8e28028
data (rvalue ref): 0x8e28038
d3 moved : 0x8e28038
So first vector was copied while the second and third were moved.
You can have two different signature to achieve move operation:
One returning an rvalue
DType data() && // rvalue
and one returing a rvalue reference
DType&& data() &&
They achieve the same result: are there any differences I cannot see ? What is the "best" one?
You can have two different signature to achieve move operation:
That is wrong.
The first signature, the one returning DType, performs a move into the return value. The second signature, the one returning DType&& simply returns a reference. It doesn't move anything.
The move happens in the other code, specifically the part with std::vector<int> d3 =. Initializing a vector from an xvalue performs a move. That is what does the move, not the function. However, other kinds of operations won't perform a move:
// no move, just binding the member to a reference
std::vector<int>&& d3 = getWidget().data(0);
Using the first function, however, the move always happens:
// move into a temporary, and bind *that* to a reference
std::vector<int>&& d2 = getWidget().data();
The second signature is dangerous. It is easy to accidentally return a reference to a temporary with it. It is easy to write misleading client code where you think something got moved out but didn't. There is one sensible use case for returning rvalue references, and that use case was already taken care of by the standard library in the forms of std::move and std::forward.
I read that template copy-con is never default copy onstructor, and template assignment-op is never a copy assignment operator.
I couldn't understand why this restriction is needed and straight away went online to ideone and return a test program but here copy constructor never gets called on further googling I came across templatized constructor and tried that but still it never calls copy constructor.
#include <iostream>
using namespace std;
template <typename T> class tt
{
public :
tt()
{
std::cout << std::endl << " CONSTRUCTOR" << std::endl;
}
template <typename U> const tt<T>& operator=(const tt<U>& that){std::cout << std::endl << " OPERATOR" << std::endl;}
template <typename U> tt(const tt<U>& that)
{
std::cout << std::endl << " COPY CONSTRUCTOR" << std::endl;
}
};
tt<int> test(void)
{
std::cout << std::endl << " INSIDE " << std::endl; tt<int> a; return a;
}
int main() {
// your code goes here
tt<int> a ; a = test();
return 0;
}
Can someone explain me the whole reason behind putting this restriction and also how to write a copy constructor of template class.
Thanks
I can't comment on why this is how it is, but here's how you write a copy constructor and assignment operator for a class template:
template <class T>
class A
{
public:
A(const A &){}
A & operator=(const A& a){return *this;}
};
and that's it.
The trick here is that even though A is a template, when you refer to it inside the class as A (such as in the function signatures) it is treated as the full type A<T>.
There are strict rules what constitutes a copy constructor (cf. C++11, 12.8):
It is not a template.
For a class T, its first argument must have type T & or T const & or T volatile & or T const volatile &.
If it has more than one argument, the further arguments must have default values.
If you do not declare a copy constructor, a copy constructor of the form T::T(T const &) is implicitly declared for you. (It may or may not actually be defined, and if it is defined it may be defined as deleted.)
(The usual overload resolution rules imply that you can have at most four copy constructors, one for each CV-qualification.)
There are analogous rules for move constructors, with && in place of &.