std :: function как пользовательский потоковый манипулятор

Я пытаюсь использовать возможности C ++ 11, чтобы упростить создание пользовательских потоковых манипуляторов. Я могу использовать лямбда-функции в качестве манипуляторов, но не std::function<ostream&(ostream&)>,

Вот код, сводится к следующему:

#include <iostream>
#include <functional>
using namespace std;

auto lambdaManip = [] (ostream& stream) -> ostream& {
stream << "Hello world" << endl;
};
function<ostream& (ostream&)> functionManip = [] (ostream& stream) -> ostream& {
stream << "Hello world" << endl;
};

int main (int argc, char** argv) {
cout << lambdaManip;    // OK
cout << functionManip;  // Compiler error
}

Второй cout Сбой заявления со следующим:

g++-4 src/Solve.cpp -c -g -std=c++0x -o src/Solve.o -I/home/ekrohne/minisat
src/Solve.cpp: In function 'int main(int, char**)':
src/Solve.cpp:24:11: error: cannot bind 'std::ostream' lvalue to 'std::basic_ostream<char>&&'
/usr/lib/gcc/i686-pc-cygwin/4.5.3/include/c++/ostream:579:5: error:   initializing argument 1 of 'std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&&, const _Tp&) [with _CharT = char, _Traits = std::char_traits<char>, _Tp = std::function<std::basic_ostream<char>&(std::basic_ostream<char>&)>]'

Почему это не удается? Я использую Cygwin GCC 4.5.3.

Пока я спрашиваю, я не схожу с ума по использованию std::function везде, из-за проблем эффективности. Но я хочу написать функции, которые возвращают лямбда-функции, и не знаю, как это сделать без std::function, Например, что-то вроде следующего было бы здорово

auto getAdditionFunctor();

auto getAdditionFunctor() {
return [] (int x, int y) { return x + y };
};

…но явно не работает. Есть ли альтернативный синтаксис, который работает? Я не могу себе представить, что это может быть, поэтому я могу застрять std::function,

Если бы у меня было решение второго вопроса, то первый вопрос был бы спорным.


Спасибо.

определяющий operator<<(ostream&, std::function<ostream&(ostream&)> помог. Я неправильно прочитал веб-страницу, и у меня сложилось впечатление, что ostream был достаточно умен, чтобы рассматривать произвольный объект, который имел operator() как манипулятор. Я был неправ по этому поводу. Кроме того, простой lambda Я построил, вероятно, только что был скомпилирован в простой старой функции, как мне сказали. Действительно, если я использую захват переменной, чтобы гарантировать, что lambda не простая функция, то компилятор не работает. Также объекты с operator() определенные (по умолчанию) не рассматриваются как манипуляторы:

class Manipulator {
ostream& operator()(ostream& stream) const {
return stream << "Hello world" << endl;
};
} classManip;

function<ostream& (ostream&)> functionManip = [] (ostream& stream) -> ostream& {
return stream << "Hello world" << endl;
};

int main (int argc, char** argv) {
const string str = "Hello world";
auto lambdaManip = [&] (ostream& stream) -> ostream& {
return stream << str << endl;
};

cout << classManip;     // Compiler error
cout << lambdaManip;    // Compiler error
cout << functionManip;  // Compiler error
}

Дальнейшее обновление: оказывается, немного более надежное решение, чем приведенное ниже, может быть достигнуто с помощью:

// Tell ostreams to interpret std::function as a
// manipulator, wherever it sees one.
inline ostream& operator<<(
ostream& stream,
const function<ostream& (ostream&)>& manipulator) {
return manipulator( stream );
}

Этот код имеет дополнительный const, Я обнаружил это, пытаясь реализовать решение в моем проекте.

9

Решение

Если вы посмотрите на operator<< за ostreamнет перегрузки для принятия std::function — и это в основном то, что вы пытаетесь сделать здесь с cout << functionManip, Чтобы это исправить, либо определите перегрузку самостоятельно:

ostream& operator<<(ostream& os, std::function<ostream& (ostream&)>& s)
{
return s(os);
}

Или передать stream в качестве аргумента функции:

functionManip(std::cout);

Что касается того, почему lambda работает, учитывая, что тип возвращаемого значения lambda не определено, и существует перегрузка для использования указателя функции:

ostream& operator<< (ostream& ( *pf )(ostream&));

Лямбда, вероятно, оборачивает все, используя структуру и определяя operator() который в этом случае будет работать точно так же, как указатель на функцию. Для меня это наиболее вероятное объяснение, надеюсь, кто-то может исправить меня, если я ошибаюсь.

7

Другие решения

Это не является причиной ошибки, но так как вы определили оба lambdaManip а также functionManip как имеющий тип возврата ostream& Я полагаю, что вы забыли добавить return stream; как для.


Вызов cout << functionManip не удается, потому что нет operator<<(ostream&, std::function<ostream&(ostream&)> определены. Добавьте один, и звонок будет успешным.

ostream& operator<<(ostream& stream, function<ostream& (ostream&)>& func) {
return func( stream );
}

Кроме того, вы можете позвонить functionManip как

functionManip( cout );

Это будет работать без добавления operator<< определение.


Что касается вашего вопроса о возвращении лямбда, так как лямбда возвращается getAdditionFunctor это лямбда без захвата, она может быть неявно преобразована в указатель на функцию.

typedef int(*addition_ptr)(int,int);
addition_ptr getAdditionFunctor()
{
return [] (int x, int y) -> int { return x + y; };
}

auto adder = getAdditionFunctor();
adder(10,20); // Outputs 30
5

В предыдущих ответах было правильно, но если вы собираетесь сильно с помощью std::functions и / или лямбды как потоковые манипуляторы в вашем коде, тогда вы можете просто добавить следующее определение шаблона функции в свою кодовую базу в глобальной области видимости:

template<class M>
auto operator<< (std::ostream& os, const M& m) -> decltype(m(os))
{
return m(os);
}

Конечный тип возврата там использует выражение сфина, так что этот конкретный operator<< перегрузка даже не будет участвовать в разрешении перегрузки, если только m(os) это правильно сформированное выражение. (Это то, что вы хотите.)

Тогда вы можете даже делать такие вещи, как

template<class T>
auto commaize(const T& t)
{
return [&t](std::ostream& os) -> std::ostream& {
return os << t << ", ";
};
}

int main()
{
std::cout << commaize("hello") << commaize("darling") << std::endl;
}

(Обратите внимание на полное отсутствие каких-либо std::function объекты в приведенном выше коде! Старайтесь не вставлять лямбды в std::function объекты, если вам абсолютно не нужно, потому что построение std::function объект может быть дорогим; это может даже включать выделение памяти.)

Для C ++ 17 имеет смысл добавить это operator<< Перегрузка в стандартную библиотеку, но я не знаю каких-либо конкретных предложений в этой области в настоящее время.

3
По вопросам рекламы [email protected]