Функция по умолчанию, которая просто возвращает переданное значение?

Как ленивый разработчик, я люблю использовать этот трюк, чтобы указать функцию по умолчанию:

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);
}

Но у меня есть проблема в очень частном случае, а именно:

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);
    }
}

В этом случае я хотел бы, чтобы функция по умолчанию была эквивалентна: [](const unsigned int i){return i;} (функция, которая просто возвращает переданное значение).

Что для этого нужно написать вместо /*SOMETHING 1*/ и /*SOMETHING 2*/?


person Vincent    schedule 04.03.2013    source источник


Ответы (6)


Стандартного функтора, который это делает, не существует, но его достаточно легко написать (хотя точная форма вызывает некоторые споры):

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

Это можно использовать следующим образом:

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);
    }
}
person Mankarse    schedule 04.03.2013
comment
+1, а скорее Function(), чем identity() в качестве аргумента по умолчанию. - person Christian Rau; 04.03.2013
comment
Почему вы используете структуру здесь? Почему бы просто не определить identity как функцию саму по себе? - person Claudiu; 15.11.2014
comment
@Claudiu: структура может быть передана как объект в метафункции (это означает, что может иметь место вывод типа для параметров шаблона, а также это означает, что встраивание упрощается для компилятора). Голая функция должна быть передана как указатель на функцию. Чтобы преобразовать шаблон функции в указатель на функцию, экземпляр шаблона должен быть создан вручную (возможно, с аргументом неизвестного типа). - person Mankarse; 15.11.2014
comment
Достаточно легко. Хе. - person Mihai Danila; 02.11.2018

Это называется функцией identity. К сожалению, он не является частью стандарта C++, но вы можете легко создать его самостоятельно.


Если вы используете g++, вы можете активировать его расширения с помощью -std=gnu++11, а затем

#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);
    }
}

Возможно, он будет доступен в C++20, см. std::identity. До тех пор вы можете посмотреть версию Boost по адресу boost::compute::identity.

person Olaf Dietsche    schedule 04.03.2013
comment
Здесь также аргумент по умолчанию, вероятно, должен быть Function() (чтобы это работало, даже когда Function не __gnu_cxx::identity<Type>). - person jogojapan; 05.03.2013

boost::phoenix предлагает полный функциональный набор инструментов, здесь 'arg1' - это идентификатор для идентификатора ;-)

#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);
  }
}
person Steig Geist    schedule 04.03.2013

Вы можете просто создать свой собственный функтор идентичности:

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);
  }
}
person Shafik Yaghmour    schedule 04.03.2013
comment
@DeadMG Спасибо, хороший комментарий, я отредактировал свой ответ, указав то, что, по моему мнению, является необходимым минимумом, не могли бы вы сообщить мне, исправит ли это все проблемы. Должен ли я использовать forward вместо move? - person Shafik Yaghmour; 04.03.2013

Существует следующий вариант с наддувом:

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))
person ομικρον    schedule 11.02.2020

Способ справиться с этим состоит в том, чтобы иметь две разные функции. Я считаю вполне разумным не использовать параметры по умолчанию.

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
}
person alfC    schedule 25.12.2018
comment
Версия C++14 интересна. Зачем нам переключаться на более длинный и трудный для чтения фрагмент кода :D - person Lightness Races in Orbit; 25.12.2018
comment
@LightnessRacesinOrbit, это просто иллюстрация для более общего контекста, где требуется истинная идентификация, общая функция пересылки. - person alfC; 25.12.2018
comment
Другими словами, это чрезмерная инженерия;) - person Lightness Races in Orbit; 25.12.2018