I want to create a temporary copy of a const object and use it in a non-const way:
struct S {
S& f() { return *this; }
};
int main() {
const S a{};
S{a}.f(); // Error on this line
return 0;
}
Using msvc (Visual Studio 2017, C++14), I get this error:
Error C2662 'S &S::f(void)': cannot convert 'this' pointer from 'const S' to 'S &'
If I change the brace initialization to classic initialization, it works:
S{a}.f(); // Does not work
S(a).f(); // Works
Both variants compile fine in gcc. Am I missing something or is this a compiler bug?
Seems like another MSVC bug.
S{a} is deduced as const struct S, and that alone is bug.
#include <string>
#include <iostream>
template < class T >
std::string type_name()
{
std::string p = __FUNCSIG__;
return p.substr( 106, p.length() - 106 - 7 );
}
struct S {
S& f() { return *this; }
};
int main() {
const S a{};
//S{a}.f(); // Error on this line
std::cout << type_name<decltype(S{a})>() << std::endl;
return 0;
}
Output:
const struct S
It looks like a compiler bug, or the result of a weird optimization, because this variation of original code that only make ctors and dtor with side effects compiles fine using MSVC:
#include <iostream>
struct S {
S(const S& other) {
std::cout << "Copy ctor " << &other << " -> " << this << std::endl;
}
S() {
std::cout << "Default ctor " << this << std::endl;
}
~S() {
std::cout << "Dtor " << this << std::endl;
}
S& f() { return *this; }
};
int main() {
const S a{};
std::cout << &a << std::endl;
S{a}.f();
return 0;
}
Compilation is successful and output is:
Default ctor 0306FF07
Copy ctor 0306FF07 -> 0306FF06
Dtor 0306FF06
Dtor 0306FF07
Related
I have a generic code at which point I leave a possibility to modify data.
if (impl_.mode() == implementation_type::manual)
{
const auto steering = custom::customize_steering(impl_, input.steering());
impl_.output_interface()(steering);
return impl_.do_continue(impl_.params().period());
}
By default I want customize_steering to be optimized out.
Now I have it this way:
template<typename ImplT, typename SteeringT>
constexpr std::decay_t<SteeringT> customize_steering(ImplT &, SteeringT &&steering)
{
return std::forward<SteeringT>(steering);
}
Is it the right way to do or is it too convoluted and passing by const reference will do fine?
I decided to check, and I don't get the results I expect.
#include <iostream>
class foo
{
public:
foo() { std::cout << "Constructed" << std::endl; }
foo(const foo &) { std::cout << "Copy constructed" << std::endl; }
foo(foo &&) { std::cout << "Move constructed" << std::endl; }
~foo() { std::cout << "Destroyed" << std::endl; }
};
template<typename T>
std::decay_t<T> do_nothing(T &&t)
{
return std::forward<T>(t);
// return t;
}
int main(int, char **)
{
const auto &fcr = do_nothing(do_nothing(do_nothing(foo())));
const auto fc = do_nothing(fcr);
return 0;
}
The output is (MinGW64):
Constructed
Move constructed
Move constructed
Move constructed
Destroyed
Destroyed
Destroyed
Copy constructed
Destroyed
Destroyed
What I expected:
Without mandatory copy elision: Constructor -> Move constructor.
With mandatory copy elision (C++17): Constructor (for const auto fc).
How can I make it work?
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 ?
}
This question already has answers here:
const reference to a temporary object becomes broken after function scope (life time)
(2 answers)
Closed 4 years ago.
For regular local const reference variables, the scope is prolonged. Which is why the following code works as expected:
#include <iostream>
#include <memory>
struct foo
{
foo()
{
std::cout << "foo() #" << (void*)this << std::endl;
}
~foo()
{
std::cout << "~foo() #" << (void*)this << std::endl;
}
};
int main()
{
auto const& f = std::make_shared<foo>();
std::cout << "f = " << f.get() << std::endl;
return 0;
}
// prints:
// foo() #0x55f249c58e80
// f = 0x55f249c58e80
// ~foo() #0x55f249c58e80
It seems though that this does not work as expected when assigning a moved object using std::move():
#include <iostream>
#include <memory>
#include <list>
struct foo
{
foo()
{
std::cout << "foo() #" << (void*)this << std::endl;
}
~foo()
{
std::cout << "~foo() #" << (void*)this << std::endl;
}
};
int main()
{
std::list<std::shared_ptr<foo>> l;
l.push_back(std::make_shared<foo>());
auto const& f = std::move(l.front());
l.clear();
std::cout << "f = " << f.get() << std::endl;
return 0;
}
// prints
// foo() #0x564edb58fe80
// ~foo() #0x564edb58fe80
// f = 0x564edb58fe80
Does std::move() indeed change the scope, or am I dealing with a compiler bug?
Changing the variable from auto const& f to just auto f fixes the problem. If I wrap the move into another function, it also works:
auto const& f = [&]() { return std::move(l.front()); }();
It's almost like std::move() does not share the same semantics as a function call, but rather as if it was just a regular variable assignment:
auto const& f = std::move(l.front());
Let's put aside std::move() and create this function:
struct sometype {};
const sometype &foobar( const sometype &cref ) { return cref; }
and now we use it:
const sometype &ref = foobar( sometype() );
What do you think, will lifetime of temporary be prolongated in this case? No it would not. Lifetime is only prolongated when you assign to a reference directly. When it goes through a function or through static_cast or std::move that prolongation is gone. So you have exactly the same issue with std::move()
struct sometype {
sometype() { std::cout << "ctype()" << std::endl; }
~sometype() { std::cout << "~ctype()" << std::endl; }
};
const sometype &foobar( const sometype &cref ) { return cref; }
int main()
{
const sometype &cref1 = foobar( sometype() );
sometype &&cref2 = std::move( sometype() );
std::cout << "main continues" << std::endl;
}
output:
ctype()
~ctype()
ctype()
~ctype()
main continues
live example
Note: you should not use std::move() on return statement, it does not give you anything.
For your code change. You should remember that std::move() does not move anything. It is done by special assignment operator or constructor (if they provided for a type). So when you write this code:
const type &ref = std::move( something );
there is no constructor nor assignment operator involved and so no moving happens. For actual moving to happen you have to assign it to a variable:
type val = std::move( something );
now moving would happen if possible or copy otherwise.
Let's consider this simple code
#include <iostream>
#include <map>
class my_class {
std::string name;
int age;
public:
my_class():name(""), age(0) {
std::cout << "Default constructor" << std::endl;
}
my_class(std::string nm, int a) : name(nm), age(a) {
std::cout << "Constructor with both parameters" << std::endl;
}
my_class(const my_class& obj) {
std::cout << "copy_constructor " << std::endl;
name = obj.name;
age = obj.age;
}
void show() {
std::cout << "name=: " << name << "age=: " << age <<std::endl;
}
};
int main() {
std::map<int, my_class> m;
m[0];
return 0;
}
I compile it via this gcc (version of my gcc: gcc version 5.4.0 20160609):
g++ -o main main.cpp
After run we see that copy constructor has been called two times:
Default constructor
copy_constructor
copy_constructor
First call of copy constructor it is because of operator[]. A call to this function is equivalent to:
(*((this->insert(make_pair(k,mapped_type()))).first)).second
So we see that make_pair calls copy constructor of my_class.
But the reason of the second call of copy constructor is not clear for me. I'm only supposing that it creates a temporary object into make_pair implementation. Am I correct ?
Say I have this function:
template <class A>
inline A f()
{
A const r(/* a very complex and expensive construction */);
return r;
}
Is it a good idea to declare r const, since a const variable cannot be moved? Note that the returned value is not const. The qualm I am grappling is, that r truly is const, but it may not be a good idea to declare it as such. Yet the qualifier should be helping the compiler generate better code.
As demonstrated here, NRVO elides the copy of r implied by the line return r;
#include <iostream>
struct A {
const char* name;
A( const char* name_ ):name(name_) { std::cout << "created " << name << "\n"; }
A(A const&){ std::cout << "copied " << name << "\n"; }
A(A &&){ std::cout << "moved " << name << "\n"; }
};
A f() {
std::cout << "start of f()\n";
A const r("bob");
std::cout << "body of f()\n";
return r;
}
int main() {
A x = f();
}
And the copy in main is also elided.
If you block NRVO and RVO in some other way (for instance using the flag -fno-elide-constructors when compiling with GCC), the const can cause your object to be copied instead of moved. You can see this if we remove the copy constructor from A:
#include <iostream>
struct A {
const char* name;
A( const char* name_ ):name(name_) { std::cout << "created " << name << "\n"; }
//A(A const&){ std::cout << "copied " << name << "\n"; }
A(A &&){ std::cout << "moved " << name << "\n"; }
};
A f() {
std::cout << "start of f()\n";
A const r("bob");
std::cout << "body of f()\n";
return r;
}
int main() {
A x = f();
}
the code no longer compiles. While the copy constructor isn't executed so long as NRVO occurs, its existence is required by your const local variable.
Now, NRVO requires a few things, such as a single variable which is returned along every single execution path of the function in question: if you ever "abort" and do a return A(), NRVO is blocked, and your const local variable suddenly forces a copy at all return sites.
If class A is under your control, and you want to return const objects by move, you can do
mutable bool resources_were_stolen = false;
and set that to true in a const move constructor
A(const A&& other) { ...; other.resources_were_stolen = true; }
~A() { if (!resources_were_stolen) ... }
Actually, the destructor probably would become if (resources_were_stolen) some_unique_ptr.release();, using the fact that objects lose their const-ness during construction and destruction.