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*/?

jogojapan
  • 68,383
  • 11
  • 101
  • 131
Vincent
  • 57,703
  • 61
  • 205
  • 388

6 Answers6

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);
    }
}
Mankarse
  • 39,818
  • 11
  • 97
  • 141
  • 3
    +1, but rather `Function()` than `identity()` as default argument. – Christian Rau Mar 04 '13 at 17:13
  • Why do you use a struct here? Why not just define `identity` as a function in and of itself? – Claudiu Nov 14 '14 at 21:33
  • 1
    @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). – Mankarse Nov 15 '14 at 11:56
  • 1
    "Easy enough". Heh. – Mihai Danila Nov 02 '18 at 15:52
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.

Olaf Dietsche
  • 72,253
  • 8
  • 102
  • 198
  • Here, too, the default argument should probably `Function()` (to allow this to work even when `Function` is _not_ `__gnu_cxx::identity`). – jogojapan Mar 05 '13 at 01:30
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);
  }
}
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);
  }
}
Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
  • @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`? – Shafik Yaghmour Mar 04 '13 at 13:54
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))
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
}
alfC
  • 14,261
  • 4
  • 67
  • 118