Я пытаюсь использовать возможности 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
, Я обнаружил это, пытаясь реализовать решение в моем проекте.
Если вы посмотрите на 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()
который в этом случае будет работать точно так же, как указатель на функцию. Для меня это наиболее вероятное объяснение, надеюсь, кто-то может исправить меня, если я ошибаюсь.
Это не является причиной ошибки, но так как вы определили оба 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
В предыдущих ответах было правильно, но если вы собираетесь сильно с помощью std::function
s и / или лямбды как потоковые манипуляторы в вашем коде, тогда вы можете просто добавить следующее определение шаблона функции в свою кодовую базу в глобальной области видимости:
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<<
Перегрузка в стандартную библиотеку, но я не знаю каких-либо конкретных предложений в этой области в настоящее время.