How to omit copying function definition for different parameters in C++ - c++

I have a lot of code that is somehow doing exactly the same operations (inherited code), and I want to, while reworking it, compress code without losing functionality. For example let's look at the following functions:
fnc(largetype & a, largetype & b) { f(A); f(B); };
fnc(largetype && a, largetype & b) { f(A); f(B); };
fnc(largetype & a, largetype && b) { f(A); f(B); };
fnc(largetype && a, largetype && b) { f(A); f(B); };
All of them are doing exactly the same thing, but the arguments can be rvalues or lvalues without breaking the function logic. I want to allow the user to pass whatever suits the problem, but I also do not want to copy-paste all code piece by piece. I can do something like this:
fnc(largetype & a, largetype & b) { f(A); f(B); };
fnc(largetype && a, largetype & b) { fnc(a,b) };
fnc(largetype & a, largetype && b) { fnc(a,b) };
fnc(largetype && a, largetype && b) { fnc(a,b) };
which is technically correct, especially with inlining, but seems wrong to me. Is there any other, nicer way to accomplish such an effect?
The only requirements are that types passed as parameters may/will be somehow larger than default memory block size so copy avoidance is crucial. Also there is a non-zero chance that parameter can be smaller but also be a rvalue. Thread safety is optional.
I considered templating these functions, but this is in my opinion also somehow the wrong approach. Templates solve problems with different accepted types. In my case the types are the same, only passed in a different way.

As #SamVarshavchik commented, this is a usual candidate for perfect forwarding. A simple approach:
template<typename T, typename U>
void fnc(T&& a, U&& b)
{
f(std::forward<T>(a));
f(std::forward<U>(b));
}
If the user passes in objects not of type largetype, the error site will be in this function, which can be confusing to the user. To push the error site to the caller's code, we can use SFINAE to constrain the parameter types:
template<
typename T, typename U,
typename = std::enable_if_t<
std::is_same<std::decay_t<T>, largetype>{}
&& std::is_same<std::decay_t<U>, largetype>{}
>
>
void fnc(T&& a, U&& b)
{
f(std::forward<T>(a));
f(std::forward<U>(b));
}
Online Demo
Alternatively, you may want to keep the error site inside of fnc but give a much clearer error message – this can be accomplished with static_assert:
template<typename T, typename U>
void fnc(T&& a, U&& b)
{
static_assert(std::is_same<std::decay_t<T>, largetype>{},
"argument 'a' must be of type 'largetype'");
static_assert(std::is_same<std::decay_t<U>, largetype>{},
"argument 'b' must be of type 'largetype'");
f(std::forward<T>(a));
f(std::forward<U>(b));
}
Online Demo

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.

Using STL algorithms on unary functions

So I often want to perform some STL algorithm on a range of elements and instead of a comparison function, I would like to pass a unary function f.
For example I would like to write something like this
std::max_element(begin(range), end(range), f);
to find the maximum element in the range after applying f.
My current workaround looks something like that:
std::max_element(begin(range), end(range, [&f](auto a, auto b){ return f(a) < f(b); });
At first glance, this may look like no problem. But f could be a lambda expression itself or in another way more complicate than just f.
I have two problem with that piece of code:
a) It is error prone because one could accidently write f(a) < f(a) (especially if more complicated and one used copy and past). This is the problem of code duplication
b) It does not express the intent very well. If I want to sort by a function, I do not want to deal with a comparison.
Unfortunately I have not found a good solution to this kind of problem in the standard library (or even boost), so I would like to ask you what your solution to this problem is. Is there any reason for the non-existence of this overload in the algorithms?
Using c++ 20's ranges you can do:
std::ranges::max_element(range | std::views::transform(f));
One thing you can do is create your own generic comparator that accepts a unary function to perform a transform:
// Generic comparator
template<typename T, typename F>
struct transform_comparator_type
{
transform_comparator_type(F f): f(f) {}
bool operator()(T const& a, T const& b) const { return f(a) < f(b); }
F f;
};
// Helper function to deduce the type of F
template<typename T, typename F>
auto transform_comparator(F f)
{
return transform_comparator_type<T, F>(f);
}
int main()
{
std::vector<int> v{1, 4, 3, 6, 0};
auto e = std::max_element(std::begin(v), std::end(v),
transform_comparator<int>([v](int i){ return 2 * i; }));
// etc...
}
Edited to add:
The type can actually be deduced from the return type of the supplied transform function, so you don't need the helper function. You can do this instead:
template<typename F>
struct transform_comparator
{
using T = decltype(F()({})); // deduce T from return type of F
transform_comparator(F f): f(f) {}
bool operator()(T const& a, T const& b) const { return f(a) < f(b); }
F f;
};

How can Boost be used to achieve C++14-style auto return types?

Suppose I have a function that adds two values together. If I know nothing about the types then I basically have to write my function twice; once in the actual return value and again as the return type specifier:
template <typename A, typename B>
auto Add(const A& a, const B& b) ->std::decay<decltype(a + b)>::type
{
return a + b;
}
While this works, it is undesirable because it is difficult to read and difficult to maintain.
In C++14 this won't be an issue, because we can drop the return type specifier (I am not sure it'll do the decay though...). For now, I'm stuck with C++11.
It has been my experience that whenever I am seeking a feature in C++ that hasn't yet made its way into the standard, but for which there is an obvious need, the Boost library usually has a solution. I have searched through the documentation, but I haven't found anything that might help me. The BOOST_AUTO_RETURN and BOOST_TYPEOF_TPL features seem more aimed at providing C++11 functionality to C++03 users.
Basically what I'm after is something that performs the following functionality:
template <typename A, typename B>
auto Add(const A& a, const B& b)
{
return a + b; // Deduce return type from this, like C++14 would
}
Is there some functionality in the Boost library that I'm unaware of (or a nifty trick in C++11) that might allow me to forego the explicit -> decltype(...) after every auto return type? How would this be implemented?
The only possible deduced function return type in C++11 is the return type of a lambda. C++11 restricts the use of lambdas, though. This works:
auto add = [](int a, int b) { return a + b; };
This is valid, and defines add as a lambda that defines an operator() member function that returns int. Since the lambda doesn't capture anything, you can even write
auto add = +[](int a, int b) { return a + b; };
to make add a regular pointer-to-function: it gets type int(*)(int, int).
However, C++11 doesn't allow parameter types to be specified as auto, nor to let add be defined as a template variable, so you cannot use this to generically deduce a return type. An attempt to wrap it up in a template class fails:
template <typename A, typename B>
struct S { static auto add = [](A a, B b) { return a + b; }; }; // invalid
It is invalid to initialise add in-class here, and you cannot use auto unless the member is initialised in-class. Besides, even if it did work, it wouldn't allow deduction of A or B, which seems to be more what you're after.
Given those limitations, I don't see any alternative but to repeat the expression. You could hide the repetition in a trivial macro, though.
#define AUTO_RETURN(func, ...) auto func -> decltype(__VA_ARGS__) { return __VA_ARGS__; }
template <typename A, typename B>
AUTO_RETURN(add(A a, B b), a + b)
Or the variant pointed out by Marc Glisse,
#define RETURNS(...) noexcept(noexcept(__VA_ARGS__)) -> decltype(__VA_ARGS__) { return __VA_ARGS__; }
template <typename A, typename B>
auto add(A a, B b) RETURNS(a + b)
which looks a bit cleaner.
There might be something like this in Boost already, I don't know. Regardless, given the triviality, Boost seems overkill here.
There is a library Pythy that tries emulate this syntax. However, it will only work on clang. It doesn't work on gcc due to these bugs here and here. They may be fixed for gcc 4.9, but if you are using gcc 4.9 you can use auto return types, anyways.

C++ Templates: Automatically overload templated function with const& for constant params?

Suppose I have a template function foo() that takes two integer references as parameters. I'd like the template function to also automatically handle constant references (such as those from constants). Here is a generalized example. I can get foo() to work, but I have to provide a new implementation for every permutation of reference/const-reference parameters.
#include <iostream>
using namespace std;
template<typename A, typename B>
void foo(A& a, B& b)
{
cout<<"a:"<<a<<" b:"<<b<<endl;
}
template<typename A, typename B>
void foo(A& a, const B& b)
{
cout<<"a:"<<a<<" b:"<<b<<endl;
}
template<typename A, typename B>
void foo(const A& a, B& b)
{
cout<<"a:"<<a<<" b:"<<b<<endl;
}
template<typename A, typename B>
void foo(const A& a, const B& b)
{
cout<<"a:"<<a<<" b:"<<b<<endl;
}
int main()
{
int x = 0;
foo(x, x);
foo(x, 10);
foo(10, x);
foo(20, 20);
return 0;
}
The above example is a little bit contrived, but it is a generalization of what I am trying to do. In my more complex case, I have a class that acts as a wrapper to a set of parameters. The class constructor is templated, like foo(), and can have as many as 10 parameters. It would be a nightmare to enumerate all 2^10 possible constructors.
The problem that you describe is perfect forwarding problem. C++11 solved this problem with universal references:
template<typename A, typename B>
void foo(A&& a, B&& b) {
bar(std::forward<A>(a), std::forward<B>(b));
}
Parameters here are not rvalue references, but universal references. They will have the same ref-ness and const-ness as arguments.
If arguments are rvalues, in foo parameters will be rvalues with names. Named rvalues are lvalues. To pass parameters to sub-functions with preserved value-ness, you need to wrap them in std::forward. Function bar will get a and b with exactly the same type as foo.
If the template is not going to modify the arguments, then just offer the version with the const& and you should be fine:
template<typename A, typename B>
void foo(const A& a, const B& b)
{
cout<<"a:"<<a<<" b:"<<b<<endl;
}
If you pass a non-const lvalue, it will still be bound by a const reference and everything will work.
If you want some of the overloads to modify the arguments, then rethink the design, as those don't seem like functions that should share a name. There are exceptions, for examples, accessors into internal members of a structure, where you might want to return a const& if the object is const or a non-const reference otherwise... If that is the case, you can go the opposite way and offer only the non-const overload:
template<typename A, typename B>
void foo(A& a, B& b)
In this case, if the argument is a temporary or a non-const reference, the deduce type will reflect it and it will bind the argument with a const&.
int main() {
int a = 5;
const int b = 10;
foo(a,b); // foo<int,const int>(int&,const int&)
foo(10,b); // foo<const int,const int>(const int&, const int&)
}
Rereading your question it seems that you might be interested in perfect forwarding (this might or not fit your bill). If that is the case, and if you have a C++11 compiler you can use universal-references with a variadic template. Building a good wrapper is a hard thing, although you might be able to just use std::tuple as the actual storage, which should make the task quite simple.
Apart from all the answers posted which are correct, here is a simple program just for your reference as this might help you to understand the concept better.
#include<iostream>
template<class X>
void func(X& x, X& y)
{
//your code
}
template<class X, class Y>
void intermediateFunc(X&& x, Y&& y)
{
func(x,y);
}
int main()
{
int y = 9;
intermediateFunc(5,5);
intermediateFunc(y,5);
intermediateFunc(5,y);
intermediateFunc(y,y);
}

Why can't we have automatically deduced return types?

Recently I was working a friend who wanted to make C++ more Haskell-y, and we wanted a function that's basically like this:
auto sum(auto a, auto b) {
return a + b;
}
Apparently I can't use auto as a parameter type, so I changed it to this:
template<class A, class B>
auto sum(A a, B b) {
return a + b;
}
But that doesn't work either. What we eventually realized we need this:
template<class A, class B>
auto sum(A a, B b) -> decltype(a + b) {
return a + b;
}
So my question is, what's the point? Isn't decltype just repeating information, since the compiler can just look at the return statement?
I considered that maybe it's needed so we can just include a header file:
template<class A, class B>
auto sum(A a, B b) -> decltype(a + b);
... but we can't use templates like that anyway.
The other thing I considered was that it might be easier for the compiler, but it seems like it would actually be harder.
Case 1: With decltype
Figure out the type of the decltype statement
Figure out the types of any return values
See if they match
Case 2: Without decltype
Figure out the types of any return values
See if they match
So with those things in mind, what's the point of the trailing return type with decltype?
Well - time passed since the original question was asked and the answer now is that you can!
Yes, it is true that the question is tagged C++11 - with which you still cannot do what the OP is asking for. But it's worthwhile to show what is doable with C++14 and later.
Since C++14 this is valid:
template<class A, class B>
auto sum(A a, B b) {
return a + b;
}
And since C++20 this is also valid:
auto sum(auto a, auto b) {
return a + b;
}
The following is the C++11 answer, kept here for historical reasons, with some comments from the future (C++14 and later):
What if we have the following:
template<class A, class B, class C>
auto sum(A a, B b, C c) {
if (rand () == 0) return a + b;
// do something else...
return a + c;
}
.. where a + b and a + c expressions yield different type of results.
What should compiler decide to put as a return type for that function and why?
This case is already covered by C++11 lambdas which allow to omit the return type as long as return statements can be deduced to the same type (NB standard quote needed, some sources claim only one return expression is allowed and that this is a gcc glitch).
A note from the future (C++14 and on): the example above is still not valid, you may only have a single possible return type. However if there are different return types but the actual return type can be deduced at compile type, then we have two different functions, which is valid. The following for example is valid since C++17:
template<class A, class B, class C>
auto sum(A a, B b, C c) {
if constexpr(std::is_same_v<A, B>) return a + b;
else return a + c;
}
int main() {
auto a1 = sum(1, 2l, 3.5); // 4.5
auto a2 = sum(1, 2, 3.5); // 3
}
Back to the original C++11 answer, explaining why the requested syntax is not supported:
A technical reason is that C++ allows the definition and declaration to be separate.
template<class A, class B>
auto sum(A a, B b) -> decltype(a + b);
template<class A, class B>
auto sum(A a, B b) -> decltype(a + b)
{
}
The definition of the template could be in the header. Or it could be in another file, so that you don't have to wade through pages and pages of function definitions when looking through an interface.
C++ has to account for all possibilities. Restricting trailing return types to just function definitions means that you can't do something as simple as this:
template<class A, class B>
class Foo
{
auto sum(A a, B b) -> decltype(a + b);
}
template<class A, class B>
auto Foo<A, B>::sum(A a, B b) -> decltype(a + b)
{
}
A note from the future (C++14 and on): you still cannot have a declaration with auto return type, if the definition is not available when the compiler sees the call.
but we can't use templates like that anyway.
First, trailing return types aren't purely a template thing. They work for all functions. Secondly, says who? This is perfectly legal code:
template<class A, class B>
auto sum(A a, B b) -> decltype(a + b);
template<class A, class B>
auto sum(A a, B b) -> decltype(a + b)
{
}
The definition of the template could be in the header. Or it could be in another file, so that you don't have to wade through pages and pages of function definitions when looking through an interface.
C++ has to account for all possibilities. Restricting trailing return types to just function definitions means that you can't do something as simple as this:
template<class A, class B>
class Foo
{
auto sum(A a, B b) -> decltype(a + b);
}
template<class A, class B>
auto Foo<A, B>::sum(A a, B b) -> decltype(a + b)
{
}
And this is fairly common for many programmers. There's nothing wrong with wanting to code this way.
The only reason lambdas get away without the return type is because they have to have a function body defined with the definition. If you restricted trailing return types to only those functions where the definition was available, you wouldn't be able to use either of the above cases.
There is no technical reason why it is not possible. The main reason they haven't is because the C++ language moves very slowly and it takes a very long time for features to be added.
You can nearly get the nice syntax you want with lambdas (but you can't have templacised arguments in lambdas, again for no good reason).
auto foo = [](int a, double b)
{
return a + b;
};
Yes, there are some cases where the return type could not be automatically deduced. Just like in a lambda, it could simply be a requirement to declare the return type yourself in those ambiguous cases.
At the moment, the restrictions are just so arbitrary as to be quite frustrating. Also see the removal of concepts for added frustration.
In a blog post from Dave Abrahams, he discusses a proposal for having this function syntax:
[]min(x, y)
{ return x < y ? x : y }
This is based off of possible proposal for polymorphic lambdas. He has also started work here on updating clang to support this syntax.