const&
and &&
can cause a combinatoric problemclass c_2 {
instrumented _a;
instrumented _b;
public:
c_2(instrumented a, instrumented b) : _a(move(a)), _b(move(b)) { }
};
instrumented v_2;
c_2 v_3{v_2, instrumented()};
namespace example_03 {
class type {
instrumented _a;
instrumented _b;
public:
template <class T, class U>
type(T&& a, U&& b) : _a(forward<T>(a)), _b(forward<U>(b)) { }
};
instrumented value;
type instance(value, instrumented());
} // namespace
Copy followed by move assignment can be more expensive than copy assignment.
class c_4 {
instrumented _a;
public:
void set(instrumented a) {
_a = move(a);
}
};
instrumented v_5;
c_4 v_6;
v_6.set(v_5);
class c_7 {
instrumented _a;
public:
template <class T>
void set(T&& a) {
_a = forward<T>(a);
}
};
instrumented v_11;
c_7 v_12;
v_12.set(v_11);
class c_3 {
instrumented _a;
instrumented _b;
public:
c_3(const instrumented& a, const instrumented& b) : _a(a), _b(b) { }
c_3(instrumented&& a, const instrumented& b) : _a(move(a)), _b(b) { }
c_3(const instrumented& a, instrumented&& b) : _a(a), _b(move(b)) { }
c_3(instrumented&& a, instrumented&& b) : _a(move(a)), _b(move(b)) { }
};
c_3 v_4{v_2, instrumented()};
template <class F, class T>
void wrapper_01(F f, T arg) {
f(arg);
}
f
is not a sink and passed an lvalue, this will cause an unnecessary copyf
is by reference, this will modify the temporary argumenttemplate <class F, class T>
void wrapper_02(F f, const T& arg) {
f(arg);
}
f
is a sink and passed an rvalue, this will cause an unnecessary copyf
is by reference, this is an errortemplate <class F, class T>
void wrapper_03(F f, T& arg) {
f(arg);
}
A forwarding reference is:
auto&&
except when deduced from a brace-enclosed initializer listnamespace {
// auto&& is a forwarding reference, not an rvalue reference
template <class F, class Arg>
void wrapper_04(F f, Arg&& arg) {
f(std::forward<Arg>(arg));
};
void f_01(instrumented){ }; // pass by value
void f_02(const instrumented&) { }; // pass by const lvalue reference
void f_03(instrumented&) { }; // pass by lvalue reference
void f_04(instrumented&&) { }; // pass by rvalue reference
} // namespace
instrumented v_03; // lvalue
wrapper_04(f_01, v_03); // call with lvalue - copy
wrapper_04(f_01, instrumented()); // call with rvalue - move
wrapper_04(f_02, v_03); // call with lvalue
wrapper_04(f_02, instrumented()); // call with rvalue
wrapper_04(f_03, v_03); // call with lvalue
// wrapper_04(f_03, instrumented()); // call with rvalue - error
// wrapper_04(f_04, v_03); // call with lvalue - error
wrapper_04(f_04, instrumented()); // call with rvalue
These are rvalue references:
namespace {
void f_0(string&&); //rvalue reference
template <class T>
class c_0 {
public:
c_0(T&&); // rvalue reference
};
} // namespace
These are forwarding references:
namespace {
template <class T>
void f_1(T&&); // forwarding reference
class c_1 {
public:
template <class T>
c_1(T&&); // forwarding reference
};
} // namespace
The difference is one special deduction rule:
string v_0 = "Hello World!";
auto&& v_1 = v_0;
if (is_lvalue_reference<decltype(v_1)>::value) {
cout << "v_1 is an lvalue reference!" << endl;
}
Pros:
Cons:
class c_5 {
instrumented _a;
public:
template <class T>
c_5(T&& a) : _a(forward<T>(a)) { }
};
c_5 v_7{instrumented()};
c_5 v_8{v_7};
input_line_21:5:18: error: no matching constructor for initialization of 'annotate'
c_5(T&& a) : _a(forward<T>(a)) { }
^ ~~~~~~~~~~~~~
input_line_22:2:6: note: in instantiation of function template specialization 'c_5::c_5<c_5 &>' requested
here
c_5 v_8{v_7};
^
./../common.hpp:5:5: note: candidate constructor not viable: no known conversion from 'c_5' to
'const annotate' for 1st argument
instrumented(const instrumented&) { std::cout << "instrumented copy-ctor" << std...
^
./../common.hpp:6:5: note: candidate constructor not viable: no known conversion from 'c_5' to 'annotate'
for 1st argument
instrumented(instrumented&&) noexcept { std::cout << "instrumented move-ctor" <...
^
./../common.hpp:4:5: note: candidate constructor not viable: requires 0 arguments, but 1 was provided
instrumented() { std::cout << "instrumented ctor" << std::endl; }
class c_6 {
instrumented _a;
public:
template <class T, class = enable_if_t<is_convertible<T, instrumented>::value>>
c_6(T&& a) : _a(forward<T>(a)) { }
};
c_6 v_9{instrumented()};
c_6 v_10{v_9};