An object can be captured by mutable reference, and changed inside a member function which takes the same object as const.
void g(const int& x, std::function<void()> f)
{
std::cout << x << '\n';
f();
std::cout << x << '\n';
}
int main()
{
int y = 0;
auto f = [&y] { ++y; };
g(y, f);
}
An object is mutated in a scope where it is const. I understand that the compiler can't enforce constness here without proving that x and y are aliases. I suppose all I'm looking for is confirmation that this is undefined behavior. Is it equivalent in some sense to a const_cast - using a value as non-const in a context where it should be?
Reference or pointer to const doesn't mean the referenced object cannot be modified at all - it just means that the object cannot be modified via this reference/pointer. It may very well be modified via another reference/pointer to the same object. This is called aliasing.
Here's an example that doesn't use lambdas or any other fancy features:
int x = 0;
void f() { x = 42; }
void g(const int& y) {
cout << y;
f();
cout << y;
}
int main() {
g(x);
}
There's nothing undefined going on, because the object itself is not const, and constness on aliases is primarily for the user's benefit. For thoroughness, the relevant section is [dcl.type.cv]p3:
A pointer or reference to a cv-qualified type need not actually point
or refer to a cv-qualified object, but it is treated as if it does; a
const-qualified access path cannot be used to modify an object even if
the object referenced is a non-const object and can be modified
through some other access path. [ Note: Cv-qualifiers
are supported by the type system so that they cannot be subverted without casting (5.2.11). —end note ]
Related
From this link it states
For example, in the code that we began with, my_vec.push_back("foo")
constructs a temporary string from the string literal, and then moves
that string into the container, whereas my_vec.emplace_back("foo")
just constructs the string directly in the container, avoiding the
extra move. For more expensive types, this may be a reason to use
emplace_back() instead of push_back(), despite the readability and
safety costs, but then again it may not. Very often the performance
difference just won’t matter
So I decided to try that and this is what i did
class foo
{
public:
int counter;
foo()
{
std::cout << "Regular constructor\n";
}
foo(const foo& f)
{
std::cout << "Copy constructor\n";
}
foo(foo&& f)
{
std::cout << "Move constructor\n";
}
};
int main()
{
std::vector<foo> f;
f.push_back(foo()); //Regular constructor and Move Constructor
f.emplace_back(foo()); //Regular constructor and Move Constructor
}
I noticed that both push_back and emplace_back behave similarly. I was thinking that emplace_back will only be calling the regular constructor based on what I read since it will be constructed in the vector stack.
vector::emplace_back(Args&&...) takes the arguments of the constructor you want to construct your new object with. In your quoted example this is const char* for the constructor string::string(const char*). In your own code you're forcing the move constructor by passing a temporary object. To default-construct your object in-place use f.emplace_back() without any arguments as the default constructor takes none.
Also to avoid reallocation (potentially more moves that would spoil your test) ensure the vector has space for your two test objects first using f.reserve(2).
Full code:
class foo
{
public:
foo()
{
std::cout << "Default constructor\n";
}
foo(const foo& f)
{
std::cout << "Copy constructor\n";
}
foo(foo&& f)
{
std::cout << "Move constructor\n";
}
};
int main()
{
std::vector<foo> f;
f.reserve(2);
f.push_back(foo());
f.emplace_back();
}
Output is
Default constructor
Move constructor
Default constructor
If a function returns a lambda that captures and mutates a value declared in the scope of the function, where/how is that value stored in memory so the lambda may safely use it?
This example is from listing 6.7 in 'Functional Programming in C++' by Ivan Čukić. It's a utility memoization method that caches results for fast lookup later. The contrived usage computes and then retrieves a cached Fibonacci number:
#include <iostream>
#include <map>
#include <tuple>
template <typename Result, typename... Args>
auto make_memoized(Result (*f)(Args...)) {
std::map<std::tuple<Args...>, Result> cache;
return [f, cache](Args... args) mutable -> Result {
const auto args_tuple = std::make_tuple(args...);
const auto cached = cache.find(args_tuple);
if (cached == cache.end()) {
auto result = f(args...);
cache[args_tuple] = result;
return result;
} else {
return cached->second;
}
};
}
unsigned int fib(unsigned int n) {
return n < 2 ? n : fib(n - 1) + fib(n - 2);
}
int main() {
auto fibmemo = make_memoized(fib);
std::cout << "fib(15) = " << fibmemo(15) << '\n';
std::cout << "fib(15) = " << fibmemo(15) << '\n';
}
My expectation was that cache would be destroyed when make_memoized returned, so a retrospective call to the lambda would have referred to a value that has gone out of scope. However it works fine (g++ 9.1 on OSX).
I can't find a concrete example of this sort of usage on cppreference.com. Any help leading me to the right terminology to search for is greatly appreciated.
The [f, cache] captures the vars by value. Once captured by value, the life of the captured var should be same as the lambda itself.
EDIT: If captured by reference (e.g. [f, &cache]), the life of cache and the lambda are no longer linked. So, while the code will still compile, it is no longer safe to use the returned lambda as cache has already been destroyed by then.
I am reading C++ Primer 5th edition and get the following problems. The book lists several cases that a synthesized move operation is defined as deleted. One of which is "Unlike the copy constructor, the move constructor is defined as deleted if the class has a member that defines its own copy constructor but does not also define a move constructor, or if the class has a member that doesn't define its own copy operations and for which the compiler is unable to synthesize a move constructor. Similarly for move-assignment."
and also provide an demo code as following:
// assume Y is a class that defines its own copy constructor but not a move constructor
struct hasY {
hasY() = default;
hasY(hasY&&) = default;
Y mem; // hasY will have a deleted move constructor
};
hasY hy, hy2 = std::move(hy); // error: move constructor is deleted
However, for both gcc 7.2.1 and clang-900.0.37, the code is runnable, is the book wrong?
Here is the complete test code:
#include <iostream>
struct Y {
Y() { std::cout << "Y()" << std::endl; }
Y(const Y&) { std::cout << "Y(const Y&)" << std::endl; }
//Y(Y&&) { cout << "Y(Y&&)" << endl; }
};
// assume Y is a class that defines its own copy constructor but not a move constructor
struct hasY {
hasY() = default;
hasY(hasY&&) = default;
Y mem; // hasY will have a deleted move constructor
};
int main() {
hasY hy, hy2 = std::move(hy); // error: move constructor is deleted
return 0;
}
The book correctly describes the behavior prescribed by the C++11 standard. The prescribed behavior, however, has changed as of C++14, which adopted the resolution of Defect Report #1402 "Move functions too often deleted"
I have the following use of std::reference_wrapper for a build in type (double) and for a user defined type (std::string).
Why do they behave differently in the case of the stream operator?
#include<functional> //reference wrapper
#include<iostream>
void fd(double& d){}
void fs(std::string& s){}
int main(){
double D = 5.;
std::reference_wrapper<double> DR(D);
std::cout << "DR = " << DR << std::endl; //ok
fd(DR); // ok
std::string S = "hello";
std::reference_wrapper<std::string> SR(S);
std::cout << "SR = " << static_cast<std::string&>(SR) << std::endl; // ok
std::cout << "SR = " << SR << std::endl; // error: invalid operands to binary expression ('basic_ostream<char, std::char_traits<char> >' and 'std::reference_wrapper<std::string>')
fs(SR); // ok
}
http://coliru.stacked-crooked.com/a/fc4c614d6b7da690
Why in the first case DR is converted to double and printed and in the second it is not? Is there a work around?
Ok, I see now, in the ostream case I was trying to called a templated function that is not resolved:
#include<functional> //reference wrapper
void double_fun(double const& t){};
template<class C>
void string_fun(std::basic_string<C> const& t){};
int main(){
double D = 5.;
std::reference_wrapper<double> DR(D);
double_fun(DR); //ok
std::string S = "hello";
std::reference_wrapper<std::string> SR(S);
string_fun(SR); // error: no matching function for call to 'string_fun'
string_fun(SR.get()); // ok
string_fun(static_cast<std::string&>(SR)); // ok
string_fun(*&SR); // would be ok if `std::reference_wrapper` was designed/coded differently, see http://stackoverflow.com/a/34144470/225186
}
For the first part TC gave you the answer. That is, operator<< for basic_string is templated, and template argument deduction doesn't look through implicit conversions.
You could alternatively call SR.get() if you don't want to explicitly to static_cast your reference wrapper.
Now for the second part, string_fun takes as input arguments std::basic_string<C> objects. When you call:
string_fun(SR);
with SR as input parameter which is of type std::reference_wrapper<std::string>, naturally you get a type mismatch.
What you can do is provide an additional overload:
template<class C>
void string_fun(std::reference_wrapper<std::basic_string<C>> const& t) {
};
Live Demo
Or if you want a more unified treatment you could define your string_fun to take template template arguments, and resolve the type with some kind of type trait magic like bellow:
template<template<typename...> class C, typename T>
void
string_fun(C<T> const &t) {
std::cout <<
static_cast<std::conditional_t<
std::is_same<
std::reference_wrapper<T>, C<T>>::value, T, std::basic_string<T>>>(t) << std::endl;
}
Live Demo
As I understand it, one cannot change the reference variable once it has been initialized. See, for instance, this question. However, here is a minmal working example which sort of does reassign it. What am I misunderstanding? Why does the example print both 42 and 43?
#include <iostream>
class T {
int x;
public:
T(int xx) : x(xx) {}
friend std::ostream &operator<<(std::ostream &dst, T &t) {
dst << t.x;
return dst;
}
};
int main() {
auto t = T(42);
auto q = T(43);
auto &ref = t;
std::cerr << ref << std::endl;
ref = q;
std::cerr << ref << std::endl;
return 0;
}
You're not changing the reference here.
You are replacing the object the reference is referring to.
In other words: after the assignment, your t is replaced by q.
ref is still a reference to t.
That does not perform a reference reassignment. Instead, it copy assigns the object in variable q into the object referenced by ref (which is t in your example).
This also justifies why you got 42 as output: the default copy assignment operator modified the first object.