У меня есть следующий код, который корректно компилируется и работает благодаря такой концепции как Манипуляторы ввода/вывода:
#include <iostream>
#include <ostream>
struct st{
auto mke(){
return [](std::ostream& os) -> decltype(auto) {return os<<42;};
}
};
int main(){
std::cout<<st{}.mke();
}
Но на самом деле в моем проекте мне нужно захватить this
по ссылке и вывести некоторые поля структуры. Что-то вроде этого:
#include <iostream>
#include <ostream>
struct st{
int a=42;
auto mke(){
return [this](std::ostream& os) -> decltype(auto) {return os<<a;};
}
};
int main(){
std::cout<<st{}.mke();
}
Но он не компилируется:
error: no match for 'operator<<' (operand types are 'std::ostream' {aka 'std::basic_ostream<char>'} and 'st::mke()::<lambda(std::ostream&)>')
12 | std::cout<<st{}.mke();
Вопрос: Мне нужно подробное объяснение причин, препятствующих компиляции второго примера. Что на самом деле произошло?
Всегда полезно включить сообщение об ошибке в вопрос.
@alter_igel Вы правы, мне нужно обновить вопрос.
Я часто обнаруживаю, что когда я пытаюсь сделать что-то странное на C++ и синтаксис становится шатким, значит, я делаю это неправильно. Какую проблему вы на самом деле пытаетесь решить с помощью этой конструкции?
В первом примере разрешение перегрузки просматривает все различные operator<<
, определенные в глобальных пространствах имен и в std
, и находит basic_ostream& operator<<(std::basic_ostream<CharT, Traits>& (*func)(std::basic_ostream<CharT, Traits>&) );
. Это жизнеспособная перегрузка, поскольку существует преобразование объекта-замыкания-лямбда без захвата в указатель на функцию (с соответствующей сигнатурой).
Во втором примере перегрузка указателя функции operator<<
невозможна, поскольку объект замыкания захватывающей лямбды не имеет преобразования указателя в функцию.
Ваш первый пример работает, потому что стандарт operator<<
принимает указатель на функцию, а ваша лямбда, не выполняющая захват, может быть преобразована в такой тип.
Ваш второй пример не удался, потому что захватывающая лямбда не может быть преобразована в указатель на функцию.
Типичный способ решить эту проблему — вообще не использовать лямбду. Вместо этого верните объект структуры/класса и перегрузите operator<<
для обработки этого типа, например:
#include <iostream>
#include <ostream>
struct st {
int a = 42;
struct manipulator {
st& m_st;
};
manipulator mke() {
return {*this};
}
};
std::ostream& operator<<(std::ostream& os, const st::manipulator &io) {
return os << io.m_st.a;
}
int main(){
std::cout << st{}.mke();
}
Я считаю, что это сводится к тому, что А) лямбды с захватами не могут быть преобразованы в указатели на функции и Б) перегрузка, которая заставляет первый случай работать с
operator<<
, ожидает указатель на функцию. Итак, на самом деле этот вопрос звучит так: «Как мне передать функтор вstd::ostream::operator<<()
?