Default function that just returns the passed value?
Asked Answered
G

6

14

As a lazy developer, I like to use this trick to specify a default function:

template <class Type, unsigned int Size, class Function = std::less<Type> >
void arrange(std::array<Type, Size> &x, Function&& f = Function())
{
    std::sort(std::begin(x), std::end(x), f);
}

But I have a problem in a very particular case, which is the following:

template <class Type, unsigned int Size, class Function = /*SOMETHING 1*/>
void index(std::array<Type, Size> &x, Function&& f = /*SOMETHING 2*/)
{
    for (unsigned int i = 0; i < Size; ++i) {
        x[i] = f(i);
    }
}

In this case, I would like the default function to be the equivalent of: [](const unsigned int i){return i;} (a function that just returns the passed value).

In order to do that, what do I have to write instead of /*SOMETHING 1*/ and /*SOMETHING 2*/?

Gemmulation answered 4/3, 2013 at 13:16 Comment(0)
U
20

There is no standard functor that does this, but it is easy enough to write (though the exact form is up for some dispute):

struct identity {
    template<typename U>
    constexpr auto operator()(U&& v) const noexcept
        -> decltype(std::forward<U>(v))
    {
        return std::forward<U>(v);
    }
};

This can be used as follows:

template <class Type, std::size_t Size, class Function = identity>
void index(std::array<Type, Size> &x, Function&& f = Function())
{
    for (unsigned int i = 0; i < Size; ++i) {
        x[i] = f(i);
    }
}
Undesigning answered 4/3, 2013 at 13:25 Comment(4)
+1, but rather Function() than identity() as default argument.Forefinger
Why do you use a struct here? Why not just define identity as a function in and of itself?Rebeckarebeka
@Claudiu: A struct can be passed as an object into metafunctions (meaning that the type deduction for the template paramenters can take place, and also meaning that inlining is easier for the compiler). A bare function would have to be passed as a function pointer. To transform a function template into a function pointer, the template would have to be manually instantiated (with a perhaps unknown type argument).Undesigning
"Easy enough". Heh.Brunell
P
3

This is called the identity function. Unfortunately, it is not part of the C++ standard, but you can easily build one yourself.


If you happen to use g++, you can activate its extensions with -std=gnu++11 and then

#include <array>
#include <ext/functional>

template <class Type, std::size_t Size, class Function = __gnu_cxx::identity<Type> >
void index(std::array<Type, Size> &x, Function&& f = Function())
{
    for (unsigned int i = 0; i < Size; ++i) {
        x[i] = f(i);
    }
}

Maybe it will be available in C++20, see std::identity. Until then you may look at boost's version at boost::compute::identity.

Pangolin answered 4/3, 2013 at 13:22 Comment(1)
Here, too, the default argument should probably Function() (to allow this to work even when Function is not __gnu_cxx::identity<Type>).Interflow
D
2

boost::phoenix offers a complete functional toolbox, here 'arg1' is the ident to identity ;-)

#include <boost/phoenix/core.hpp>

template <class X, class Function = decltype(boost::phoenix::arg_names::arg1)>
void index(X &x, Function f = Function()) {
    for (std::size_t i = 0; i < x.size(); ++i) {
            x[i] = f(i);
  }
}
Dolli answered 4/3, 2013 at 15:14 Comment(0)
B
1

You can just build your own identity functor:

template <typename T>
class returnIdentifyFunctor
{
  public:
     auto operator ()(  T &&i ) -> decltype( std::forward<T>(i) )
    {
      return std::move(i);
    }
};

template <class Type, unsigned int Size, class Function = returnIdentifyFunctor<Type>>
void index(std::array<Type, Size> &x, Function&& f = Function() )
 {
    for (unsigned int i = 0; i < Size; ++i) {
            x[i] = f(i);
  }
}
Beilul answered 4/3, 2013 at 13:24 Comment(1)
@DeadMG Thank you, good comment, I edited my answer with what I think is the minimum needed, can you let me know if that fixes all the issues. Should I use forward instead of move?Beilul
F
1

There is the following variant with boost:

template <class Type, unsigned int Size, class Function = boost::function<Type(Type)>>
void index(std::array<Type, Size> &x, Function&& f = boost::bind(std::plus<Type>(), 0, _1))
Freedwoman answered 11/2, 2020 at 18:18 Comment(0)
A
0

A way to deal with this is to have two different functions. I find quite sane not to use default parameters.

template <class Type, unsigned int Size, class Function>
void index(std::array<Type, Size> &x, Function&& f){
    for(unsigned int i = 0; i < Size; ++i) x[i] = f(i);
}

template<class Type, unsigned int Size>
void index(std::array<Type, Size> &x){
    return index(x, [](unsigned int i){return i;});                      // C++11 in this case
                //, [](auto&& e){return std::forward<decltype(e)>(e)};); // C++14 in a more general case
                //, std::identity); // C++20 in general
}
Ayo answered 25/12, 2018 at 0:15 Comment(3)
C++14 version is interesting. Why would we switch to a longer and harder-to-read piece of code :DCircumgyration
@LightnessRacesinOrbit, it is just to illustrate the point for a more general context where a true identity, generic, forwarding function is required.Ayo
So in other words it's over-engineering ;)Circumgyration

© 2022 - 2024 — McMap. All rights reserved.