Consider the following program:
#include <iostream>
struct X {
X () = default;
X (X &) { std::cout << "non-const called" << std::endl; }
X (X const &) { std::cout << "const called" << std::endl; }
int i () const { return 7; }
};
auto f () {
X x;
auto lambda = [=]() { return x.i(); };
return lambda;
}
int main()
{
auto lambda = f();
std::cout << lambda () << std::endl;
return 0;
}
With VC++15, I get the output
const called
const called
7
With Clang 3.9, I get
non-const called
7
Which compiler is correct here?
I would say that clang is right.
The most suited constructor is called only once when the lambda captures x and the constructor for the returned value is optimized out.
That's why you obtain only one non-const called.
See here and here for further details about copy-elision and RVO.
Related
I have a method that modifies objects passed by reference:
class MyClass;
MyClass& modify (MyClass& x) { ...; return x; }
What's the right way to extend modify to unnamed objects avoiding extra copies, so that the following code is valid?
MyClass createMyClass () { ... }
MyClass x = modify(createMyClass());
// instead of:
MyClass x = createMyClass();
modify(x);
PS: MyClass implements efficient moving.
It seems this code works with perfect forwarding, but not sure if you like it or not:
#include <memory>
#include <iostream>
struct T {
T() {std::cout << this << " ctor\n";}
~T() {std::cout << this << " dtor\n";}
T(const T& t) {std::cout << this << " cc\n"; i = t.i;}
T(T&& t) {std::cout << this << " mc\n"; i = t.i;}
T& operator=(const T& t) {std::cout << this << " ca\n"; i = t.i; return *this;}
T& operator=(T&& t) {std::cout << this << " ma\n"; i = t.i; return *this;}
int i = 0;
};
//T& modify (T& t) {t.i++; return t; }
template<typename X>
X modify (X&& t) { t.i++; return t; }
T create_T () { return T{}; }
int main() {
T t1 = modify(create_T());
std::cout << t1.i << "\n----------\n";
// instead of:
T t2 = create_T();
modify(t2);
std::cout << t2.i << "\n";
return 0;
}
And output will be like this:
0x7fff616d0fe0 ctor
0x7fff616d0fe8 mc
0x7fff616d0fe0 dtor
1
----------
0x7fff616d0fd8 ctor
1
0x7fff616d0fd8 dtor
0x7fff616d0fe8 dtor
You can also replace prefect forwarding with these 2 lines:
T& modify (T& t) { t.i++; return t; }
T modify (T&& t) { t.i++; return t; }
but not very interesting method.
Update:
For the reason that I don't know, following code does not generate similar output like perfect forwarding:
auto modify (auto&& t) { t.i++; return t; }
and output will be like this:
0x7ffcfa65c9a8 ctor
0x7ffcfa65c9b0 mc
0x7ffcfa65c9a8 dtor
1
----------
0x7ffcfa65c9a8 ctor
0x7ffcfa65c9b8 cc
0x7ffcfa65c9b8 dtor
1
0x7ffcfa65c9a8 dtor
0x7ffcfa65c9b0 dtor
Maybe the more experienced ones can tell the reasoning behind this difference.
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?
After researching a bit I don't understand the output (source code below):
42
42
45
I'm fine with the second, but why I get this output?
It's coming from fooling around with avoiding global constants and variables in a bigger project. Could someone please explain it to me?
#include <iostream>
class Const
{
public:
Const() = delete;
static auto foo(int val = 42) -> int&;
};
auto Const::foo(int val) -> int&
{
static int sval = val;
return sval;
}
int main()
{
std::cout << Const::foo() << std::endl;
Const::foo(24);
std::cout << Const::foo() << std::endl;
Const::foo() = 45;
std::cout << Const::foo() << std::endl;
return 0;
}
In your code:
#include <iostream>
class Const // what a strange name for something that is not const.
{
public:
Const() = delete;
static auto foo(int val = 42) -> int&;
};
auto Const::foo(int val) -> int& // if you don't return a const reference,
// it may be modified by the caller
{
static int sval = val; // val is not const, and only initialized
// once.
return sval; // you return a reference to a mutable value.
}
int main()
{
std::cout << Const::foo() << std::endl;
Const::foo(24); // this changes nothing, the static variable sval
// has already been initialized.
std::cout << Const::foo() << std::endl;
Const::foo() = 45; // the reference returned by foo() is
// not const. SO that works.
std::cout << Const::foo() << std::endl;
return 0;
}
To fix, Const::foo() should return a const int&.
Forget about using static function variables. When entering the function, the code must check each time if its static variables have been initialized. This usually involves using a hardware fence or some other thread-safe mechanism. These will unnecessarily slow down execution, especially when accessing these const values from multiple threads.
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.
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