C++ overloading operator, constant parameter or pass by value? - c++

template <typename T>
T operator+(T a, const T& b) {
a += b;
return a;
}
template <typename T>
T operator+(const T& a, const T& b) {
T tmp {a};
tmp += b;
return tmp;
}
Is there any reason why you would pass argument as constant reference like the second function, over directly passing by value like the first function, since you need a temporary variable anyway?
Edit 1:
I think I should mention that these 2 functions alternative are just for handling case with lvalue arguments, and that I am to provide 2 other functions for handling with rvalue arguments, as follows.
template <typename T>
T operator+(T&& a, const T& b) {
a += b;
return std::move(a);
}
template <typename T>
T operator+(const T& a, T&& b) {
b += a;
return std::move(b);
}
So the emphasis of the question is, why would I need to explicitly create a temporary variable (function 2), when I can just let the language facilitate that automatically for me (function 1)?

template <typename T>
T operator+(T a, const T& b) {
a += b;
return a;
}
In here you are making a copy of variable a which is passed in here then you are updating a copy. which requires three copies to be created, and again you are returning by value.
template <typename T>
T operator+(const T& a, const T& b) {
T tmp {a};
tmp += b;
return tmp;
}
in here your tmp variable has a local scope and variable a is const reference so no modification to the value of a is allowed.
And you are returning a copy of temp which is a local variable.
Both work fine but the difference is in the number of copies created. you are making more copies in 1 st case than of the second.
Although the second one will be optimized for tmp variable to use move semantics in order to make fewer copies. so you will have faster performance in 2nd case

Related

C++ passing by const ref vs universal ref

So I've recently learned about universal references and reference collapsing.
So let's say I have two different implementations of a max function like such.
template<class T>
T&& max(T&& a, T&& b)
{
return (a < b) ? b : a;
}
template<class T>
const T& max(const T& a, const T& b)
{
return (a < b) ? b : a;
}
One version takes its arguments by const reference and other takes them by universal reference.
What confuses me about these is when should either be used. Passing by const is mainly used so you can bind temporaries to it.
In the stl there's different uses as well. std::move takes universal reference where std::max takes by const ref.
What's the situations on where either should be used??
Say if I wanted to avoid the copy when it returns or keep a reference on return. Would it makes sense to have a const and nonconst version of the function etc
The first one should probably be:
// amended first version
template<class T>
decltype(auto) max(T&& a, T&& b) {
return (a < b) ? std::forward<T>(b) : std::forward<T>(a);
}
Otherwise it wouldn't work for temporaries (the original first option fails with two temporaries).
But even now, with the new version above, it cannot work for a mix of an rvalue and an lvalue:
int i = my_max(3, 5); // ok
i = my_max(i, 15); // fails
// no known conversion from 'int' to 'int &&' for 1st argument
To add to that, the return value of the first option is either an lvalue-ref or an rvalue-ref (with or without const, depending on the arguments). Getting back rvalue-ref to a temporary would not be the best idea, it would work if we immediately copy from it, but then it would be better to return byvalue, which is not the approach taken by the first version.
The pitfall with the second, the const-lvalue-ref version, is that if a temporary is sent to it, we return a const-lvalue-ref to a temporary which is bug prone (well, not more than returning an rvalue-ref to a temporary, but still). If you copy it immediately you are fine, if you take it by-ref you are in the UB zone:
// second version
template<class T>
const T& max(const T& a, const T& b) {
return (a < b) ? b : a;
}
std::string themax1 = max("hello"s, "world"s); // ok
const std::string& themax2 = max("hello"s, "world"s); // dangling ref
To solve the above problem, with the cost of redundant copying for the lvalue-ref case, we can have another option, returning byvalue:
// third version
template<class T1, class T2>
auto max(T1&& a, T2&& b) {
return (a < b) ? std::forward<T2>(b) : std::forward<T1>(a);
}
The language itself took the 2nd option for std::max, i.e. getting and returning const-ref. And the user shall be careful enough not to take a reference to temporaries.
Another option might be to support both rvalue and lvalue in their own semantic, with two overloaded functions:
template<class T1, class T2>
auto my_max(T1&& a, T2&& b) {
return (a < b) ? std::forward<T2>(b) : std::forward<T1>(a);
}
template<class T>
const T& my_max(const T& a, const T& b) {
return (a < b) ? b : a;
}
With this approach, you can always get the result as a const-ref: if you went to the first one you get back a value and extend its lifetime, if you went to the second one you bind a const-ref to a const-ref:
int i = my_max(3, 5); // first, copying
const int& i2 = my_max(i, 25); // first, life time is extended
const std::string& s = my_max("hi"s, "hello"s); // first, life time is extended
const std::string s2 = my_max("hi"s, "hello"s); // first, copying
const std::string& s3 = my_max(s, s2); // second, actual ref, no life time extension
The problem with the above suggestion is that it only takes you to the lvalue-ref version if both arguments are const lvalue-ref. If you want to cover all cases you will have to actually cover them all, as in the code below:
// handle rvalue-refs
template<class T1, class T2>
auto my_max(T1&& a, T2&& b) {
return (a < b) ? std::forward<T2>(b) : std::forward<T1>(a);
}
// handle all lvalue-ref combinations
template<class T>
const T& my_max(const T& a, const T& b) {
return (a < b) ? b : a;
}
template<class T>
const T& my_max(T& a, const T& b) {
return (a < b) ? b : a;
}
template<class T>
const T& my_max(const T& a, T& b) {
return (a < b) ? b : a;
}
template<class T>
const T& my_max(T& a, T& b) {
return (a < b) ? b : a;
}
A last approach to achieve the overloading, and supporting all kind of lvalue-ref (const and non-const, including a mixture), without the need for implementing the 4 combinations for lvalue-ref, would be based on SFINAE (here presented with C++20 with a constraint, using requires):
template<class T1, class T2>
auto my_max(T1&& a, T2&& b) {
return (a < b) ? std::forward<T2>(b) : std::forward<T1>(a);
}
template<class T1, class T2>
requires std::is_lvalue_reference_v<T1> &&
std::is_lvalue_reference_v<T2> &&
std::is_same_v<std::remove_cvref_t<T1>, std::remove_cvref_t<T2>>
auto& my_max(T1&& a, T2&& b) {
return (a < b) ? b : a;
}
And if you bear with me for one past the last... (hope you are not in a bucket for monsieur state by now). We can achieve it all in one function!, and as a side benefit, even allow cases of comparison between derived and base if supported, so the following would work fine:
A a = 1;
B b = 2; // B is derived from A
// if comparison between A and B is supported, you can do
const A& max1 = my_max(a, b); // const-ref to b
const A& max2 = my_max(a, B{-1}); // const-ref to a temporary copied from a
const A& max3 = my_max(a, B{3}); // const-ref to B{3}
This would be the code to support this with a single function:
template<typename T1, typename T2>
struct common_return {
using type = std::common_reference_t<T1, T2>;
};
template<typename T1, typename T2>
requires std::is_lvalue_reference_v<T1> &&
std::is_lvalue_reference_v<T2> &&
has_common_base<T1, T2> // see code in link below
struct common_return<T1, T2> {
using type = const std::common_reference_t<T1, T2>&;
};
template<typename T1, typename T2>
using common_return_t = typename common_return<T1, T2>::type;
template<class T1, class T2>
common_return_t<T1, T2> my_max(T1&& a, T2&& b)
{
if(a < b) {
return std::forward<T2>(b);
}
return std::forward<T1>(a);
}
The machinery for the above can be found here.
For the variadic version of this, you can follow this SO post.

Templated class operator+ overloading return type

I am trying to built a templated num class. This class needs to have a public attribute, val, with type T, which is the only templated parameter. Furthermore if one provides a value the attribute (val) should be initialized with this value. To do so I made the following code:
#include <iostream>
template<class T>
class Num {
public:
T val;
Num():val(0) { std::cout<<"default constr used"<<std::endl; }
Num(T value):val(value) {std::cout<<"constr (T value) used"<<std::endl; }
~Num() { std::cout<<"destructor used"<<std::endl; }
template<typename U>
Num operator+(const Num<U>& other) {
return val+other.value;
}
};
Furthermore I created the main() function to test the program, which looks like this:
int main() {
std::cout << Num<int>(1) + Num<double>(2.0);
return 0;
}
However the result of the program is now 3. Whereas I expected it to be 3.0 (of type double).
For that you will need to change the return type.
In your code:
// vvv---- Means Num<T>
Num operator+(const Num<U>& other) {
return val + other.val;
}
Indeed, inside a class template, you can type the name of the class without template arguments and it's gonna be somewhat equivalent to writing Num<T>.
Your function is always returning the type of the first operant, no matter the type of the addition itself.
What you want is to deduce that type coming from the addition:
auto operator+(const Num<U>& other) -> Num<decltype(val + other.val)> {
return val + other.val;
}
That way, it's always the right return type according to the C++ operator rules.
operator+ should be symmetric with respect to its arguments. It's better be implemented as a free function rather than a member function to make this symmetry explicit.
For example (using C++14 return type deduction):
template<class T, class U>
auto operator+(const Num<T>& x, const Num<U>& y) {
using R = decltype(std::declval<T>() + std::declval<U>());
return Num<R>{x.val + y.val};
}
std::declval<T>() is there for genericity, if T and/or U are not default constructible. If types are limited to built-in ones, like int and double, it can be replaced with T{} or T():
using R = decltype(T{} + U{});
With class template argument deduction in C++17 it can be simplified further:
template<class T, class U>
auto operator+(const Num<T>& x, const Num<U>& y) {
return Num{x.val + y.val};
}

Why is the const&& overload of as_const deleted?

On a blog on the progress of C++17 I read the following:
P0007 proposes a helper function template as_const, which simply
takes a reference and returns it as a reference to const.
template <typename T> std::add_const_t<T>& as_const(T& t) { return t }
template <typename T> void as_const(T const&&) = delete;
Why is the const&& overload deleted?
Consider what would happen if you didn't have that overload, and try to pass in a const rvalue:
template <typename T> const T &as_const(T &t) { return t; }
struct S { };
const S f() { return S{}; }
int main() {
// auto & ref = as_const(S()); // correctly detected as invalid already
auto & ref = as_const(f()); // accepted
}
This would be accepted because T would be deduced as const S, and temporaries can bind to const S &. The result would be that you accidentally get an lvalue reference to a temporary, which will be destroyed right after ref has been initialised. Almost all users taking lvalues (whether variables or function parameters) don't expect to be passed temporaries; silently accepting temporaries means that you easily silently get dangling references.

Replicate Haskell's Return Type Overloading (via Typeclasses) in C++

In Haskell, typeclasses allow for you to elegantly overload functions based on return type. It is trivial to replicate this in C++ for cases where both the arguments and the return type are overloaded, using templates (example A):
template <typename In, typename Out> Out f(In value);
template <typename T> int f<T, int>(T value) {
...
}
Which corresponds to Haskell's:
class F a b where
f :: a -> b
You can even overload just the return type on most functions (example B):
template <typename Out> Out f(SomeClass const &value);
template <> inline int f(SomeClass const &value) {
return value.asInt();
}
template <> inline float f(SomClass const &value) {
return value.asFloat();
}
Which corresponds to something like:
class F a where
f :: SomeData -> a
But what I would like to be able to do is modify this last example to overload on higher-order types, namely templated structs in C++. That is, I'd like to be able to write a specialization akin to the following Haskell:
data Foo a = Foo a
instance F (Foo a) where
f someData = Foo $ ...
How would one go about writing a template with this functionality (is it even possible)?
For reference, I'm intending to use this to write template functions for Lua/C++ bridge. The idea is to bridge between Lua and C++ functions by having an overloaded function interpretValue that automatically pushes or converts from the Lua stack. For simple types that have a direct built-in Lua representation, this is easy enough using code such as example B.
For more complicated types, I'm also writing a template <typename T> struct Data to handle memory management for objects (bridging between Lua's GC and a C++ side refcount), and I was hoping to be able to overload interpretValue so that it can automatically wrap a userdata pointer into a Data<T>. I tried using the following, but clang gave a "function call is ambiguous" error:
template <typename U> inline U &interpretValue(lua_State *state, int index) {
return Data<U>::storedValueFromLuaStack(state, index);
}
template <typename U> inline Data<U> interpretValue(lua_State *state, int index) {
return Data<U>::fromLuaStack(state, index);
}
Thanks!
Well, you can write one function:
template <class U>
interpretValueReturnType<U> interpretValue(lua_State *state, int index)
{
return interpretValueReturnType<U>(state, index);
}
Then, you need to write this return type with casting operators, so, you get what you want:
template <class U>
class interpretValueReturnType
{
public:
interpretValueReturnType(lua_State *state, int index) : state(state), index(index) {}
operator U& () &&
{
return Data<U>::storedValueFromLuaStack(state, index);
}
operator Data<U> () &&
{
return Data<U>::fromLuaStack(state, index);
}
private:
lua_State *state;
int index;
};
See ideone:
int main() {
lua_State *state;
int& a = interpretValue<int>(state, 1);
Data<int> b = interpretValue<int>(state, 1);
}
This funny && at the end of operators declarations are for making a little hard to store result of this function and use it later - like here:
auto c = interpretValue<float>(state, 1);
float& d = c; // not compile
One'd need to use std::move because && means that function can be used only for rvalue references:
auto c = interpretValue<float>(state, 1);
float& d = std::move(c);

Function template won't compile

I am just learning function templates and the template I created below doesn't compile and I am not sure what's wrong. I am trying to make an int variable and a double variable go into the template but I keep getting an error when I call the function. The error is:
error: no matching function for call to 'LargestFunction(int&, double&)'|
and the code is as follows:
template <class Temp>
Temp LargestFunction(Temp a, Temp b){
if(a > b){
return a;
}
else
return b;
}
int main()
{
int NumOne = 30;
double NumTwo = 52.252;
cout << LargestFunction(NumOne,NumTwo);
return 0;
}
If you want to support different types, you need to define the template with different template parameters.
template <typename Lhs, typename Rhs>
typename std::common_type<Lhs, Rhs>::type max(const Lhs &lhs, const Rhs &rhs) {
return lhs > rhs ? lhs : rhs;
}
This way you can pass different types and you'll get whatever the common type between them is.
If you want to only deal with equal types within the function, you can keep the template as-is.
template <typename T>
T max(const T &lhs, const T &rhs) {
return lhs > rhs ? lhs : rhs;
}
You need to then cast one of the parameters so that you have equal types.
max(static_cast<double>(101), 4.2);
Or, you could also explicitly specialize the function template, but this is discouraged in general.
max<double>(101, 4.2);
The compiler doesn't know which type it should infer. Should it be int or double? In your template, Temp refers to a single type. You are free to specify it by calling:
LargestFunction<double>(NumOne,NumTwo);
Or you could define your template to take Temp1 and Temp2 types.