auto
auto
Used to denote that a variable has automatic storage.
In C++ all variables have automatic storage by default.
auto
In C++11 it gets a new meaning - tells the compiler to deduce the type of the variable. This is called type inference
int x = 42;
std::string s = "the answer is";
auto y = 2 * x; // 2 is int, int * int = int, so y is int
auto t = s + "!"; // std::string + "!" is string, so t is string
auto
BOOL result = ::CreateProcess(...);
if (!result)
{
DWORD error = ::GetLastError();
std::cout << "Could not create process " << error
<< std::endl;
}
// don't care what type it is as long as it behaves as a bool
auto result = ::CreateProcess(...);
if (!result)
{
// don't care what type it is as long as it can be logged
auto error = ::GetLastError();
std::cout << "Could not create process " << error
<< std::endl;
}
Bonus: I don't get shouted at
std::vector<int> v;
// ...
auto begin = v.cbegin();
std::vector<int>::const_interator begin2 = v.cbegin();
auto
gotchasstruct Course {
std::vector<int>& get_students() const;
}
// ...
auto students = course.get_students();
course.get_students()[0] = 24;
students[0] = 42;
std::cout << course.get_students()[0] << std::endl;
// it is still 24
// students is actually a copy
auto& students = course.get_students();
course.get_students()[0] = 24;
students[0] = 42;
std::cout << course.get_students()[0] << std::endl;
// it is 42
The difference is only the & after auto
Taking a const reference with auto
const auto& students = course.get_students();
RVO
std::vector<int> get_random_ints(int count);
// ...
auto numbers = get_random_ints(1000);
You can get advice to not return such expensive to copy things by
valuevoid get_random_ints(int count, std::vector<int>& numbers);
// ...
std::vector<int> numbers;
get_random_ints(1000, numbers);
Compilers are allowed to make any optimization as long as the program behaves in the same way as if there are no optimizations
C++ allows compilers to implement RVO even breaking this rule
std::vector<int> get_random_ints(int count);
// ...
auto numbers = get_random_ints(1000);
Gets compiled as
void get_random_ints(int count, std::vector<int>& numbers);
// ...
std::vector<int> numbers;
get_random_ints(1000, numbers);
std::vector<int> get_numbers() {
std::vector<int> result;
// ...
return (rand() % 2)? result : std::vector<int>();
}
Instead of copying values move them directly into their new variables
In C++ it is easy to create unnecessary copies of object, hurting performance.
std::string s = "Hello";
std::string t = "World";
std::string m = s + t;
s
is concatenated with t
in a temporary string
which is then copied into m
assuming that the compiler will not optimize the code
operator =
operator =
T&&
can be both - lvalue and rvalueDerived(Derived&& other)
: Base(other) // wrong: other is an lvalue --> copies the Base part
{
// ...
}
Derived(Derived&& other)
: Base(std::move(other)) // :)
{
// ...
}
T&
T&&
const T&
Can reference everything - values stored in variables, result values of expressions.
const T&&
Can reference only result values of expressions
void function(const std::string& lvalue); // lvalue overload
void function(const std::string&& rvalue); // rvalue overload
std::string s = "Hello World";
function(s); // lvalue overload
function("Hello World"); // rvalue overload
// the const char* is converted to a temporary string which can be
// rvalue
class String {
public:
String(const char* s)
: m_Length(std::strlen(s))
, m_Buffer(new char[m_Length])
{
std::memcpy(m_Buffer, s, m_Length);
}
~String() {
delete [] m_Buffer;
}
private:
size_t m_Length;
char* m_Buffer;
};
String(const String& o)
: m_Length(o.m_Length)
, m_Buffer(new char[m_Length])
{
std::memcpy(m_Buffer, o.m_Buffer, m_Length);
}
Create a copy of the other string
class String {
// ...
String(String&& o)
: m_Buffer(o.m_Buffer)
, m_Length(o.m_Length)
{
o.m_Buffer = nullptr;
o.m_Length = 0;
}
String(const String&& o)
Moves the value of the other string into this object
String s("Hello");
String t("World");
String s2 = s; // copy
String t2 = s + t; // move
std::move
Tells the compiler to create an rvalue reference for an lvalue. Allows us to move ordinary values stored in variables.
template <typename T>
T&& std::move(T& lvalue);
String s("Hello");
String t = std::move(s); // move s into t
// !!! Undefined behavior !!!
std::cout << s << std::endl;
// We have moved s into t, it's value is unknown
class String {
// ..
String& operator= (const String& rhs) {
if (this != &rhs) {
delete [] m_Buffer;
m_Length = rhs.m_Length;
m_Buffer = new char[rhs.m_Length];
std::memcpy(m_Buffer, rhs.m_Buffer, m_Length);
}
return *this;
}
before
after
class String {
// ..
// move assignment
String& operator= (String&& rhs) {
if (this != &rhs) {
delete [] m_Buffer;
m_Length = rhs.m_Length;
m_Buffer = rhs.m_Buffer;
rhs.m_Length = 0;
rhs.m_Buffer = nullptr;
rhs.m_Length = 0;
}
return *this;
}
};
before
after
String s = "Hello";
String t = "World";
String m = s + t; // 1
m = t + s; // 2
m = t; // 3
m = std::move(s); // 4
class String {
// ..
// move assignment
String& operator= (String&& rhs) {
std::swap(m_Buffer, rhs.m_Buffer);
std::swap(m_Length, rhs.m_Length);
return *this;
}
};
before
after
in the end
So that their destructor will run correctly.
template <typename T, typename Arg1>
T factory(/*const*/ Arg1 /*&*/ arg1)
{
return T(arg1);
}
Arg1 arg1
Arg1& arg1
const Arg1& arg1
Arg1 arg1
Arg1& arg1
const Arg1& arg1
const
reference/pointer to the
valuetemplate <typename T, typename Arg1>
T factory(Arg1& arg1);
template <typename T, typename Arg1>
T factory(const Arg1& arg1);
// How about
template <typename T, typename Arg1, typename Arg2>
T factory(const Arg1& arg1, const Arg2& arg2);
&&
T& + &
--> T&
T& + &&
--> T&
T&& + &
--> T&
T&& + &&
--> T&&
template <typename T, typename Arg1>
T factory(Arg1&& arg1)
{
return T(std::forward<Arg1>(arg1));
}
std::forward
template<class S>
S&& forward(typename remove_reference<S>::type& a) noexcept
{
return static_cast<S&&>(a);
}
http://thbecker.net/articles/rvalue_references/section_01.html