Wyrażenie lambda sprawiające kłopot - co robi ' -> '

0
void NegativeLambda(cv::Mat& image)
{
	typedef cv::Vec<uchar, 3> Pixel;

	image.forEach<Pixel>([](Pixel &p, const int* position) -> void {
	p.val[0] = 255 - p.val[0];
	p.val[1] = 255 - p.val[1];
	p.val[2] = 255 - p.val[2];
	});
}

Mam tu taką funkcję. Mam pytanie o co chodzi w '->' ?
Do tej pory znałem to jako operator odwołania się do wartości wskaźnika.

4

Jest elementem składniowym opisującym typ zwracany z funkcji.

<@KrzaQ> << is_same<a, b>{}; using a = int(int); using b = auto(int) -> int;
<+cxx> true
3

Niby lambda jest po to, żeby od razu ją zdefiniować w miejscu wywołania, ale ja zawsze wolę poświęcić jedną linię, żeby taką lambdę przypisywać do zmiennej (która może mieć nazwę wyjaśniającą co ta lambda robi) i stosować trochę przystępniejsze wcięcia

void NegativeLambda(cv::Mat& image)
{
    typedef cv::Vec<uchar, 3> Pixel;
    
    auto negate = [](Pixel &p, const int* position) -> void 
                  {
                      p.val[0] = 255 - p.val[0];
                      p.val[1] = 255 - p.val[1];
                      p.val[2] = 255 - p.val[2];
                  }

    image.forEach<Pixel>(negate);
}
2

Tej strzałki można użyć także do każdej normalnej funkcji:

auto main() -> int
{
}

to to samo co

int main()
{
}

Podobnie:

class Klasa
{
	auto foobar() -> void;
};

auto Klasa::foobar() -> void
{
}
1
twonek napisał(a):

Niby lambda jest po to, żeby od razu ją zdefiniować w miejscu wywołania, ale ja zawsze wolę poświęcić jedną linię, żeby taką lambdę przypisywać do zmiennej (która może mieć nazwę wyjaśniającą co ta lambda robi) i stosować trochę przystępniejsze wcięcia

Osobiście nie cierpię takiego podejścia. Dla mnie lambda jest adaptorem do argumentu funckji/algorytmu.
Jak ja używam lambdy to ma 1 - 3 linijki (ma być krótsza od moich krótkich funkcji).
Jeśli lambda potrzebuje nazwy, bo jest długa, to znaczy że tak naprawdę jest to funkcja/klasa, którą normalnie definiuję, bez kombinowania z lambdą.
Jeśli kod wymusza użycia lambdy to i tak definiuję funkcję, którą potem opakowuję lambdą która ma jedną linijkę.

Z mojej obserwacji wynika, że długie lambdy w funkcji są bardzo rozpraszające. Często się łapie na tym, że czytam cudzy kod i nie mogę go zrozumieć, by potem zauważyć: "a ten kod nie wykonuje się tutaj, ale w innym miejscu, bo to jest lambda".

1
MarekR22 napisał(a):
twonek napisał(a):

Niby lambda jest po to, żeby od razu ją zdefiniować w miejscu wywołania, ale ja zawsze wolę poświęcić jedną linię, żeby taką lambdę przypisywać do zmiennej (która może mieć nazwę wyjaśniającą co ta lambda robi) i stosować trochę przystępniejsze wcięcia

Osobiście nie cierpię takiego podejścia. Dla mnie lambda jest adaptorem do argumentu funckji/algorytmu.
Jak ja używam lambdy to ma 1 - 3 linijki (ma być krótsza od moich krótkich funkcji).
Jeśli lambda potrzebuje nazwy, bo jest długa, to znaczy że tak naprawdę jest to funkcja/klasa, którą normalnie definiuję, bez kombinowania z lambdą.
Jeśli kod wymusza użycia lambdy to i tak definiuję funkcję, którą potem opakowuję lambdą która ma jedną linijkę.

Rozumiem że podobnie nie cierpisz definiowania stałych ani zmiennych, bo lepiej używać wyłącznie magicznych wartości i wyrażeń w miejscu użycia? ;-)

Bo problem sprowadza się do sytuacji

foo(123);

vs

int someValue = 123;
foo(someValue);

Uważam że kod jest czystszy jeśli lambda ma własną nazwę, niż jeśli jest wklejona w dziwaczny sposób wewnątrz wywołania funkcji, z kumulacją nawiasów i nieczytelnych wcięć.

foo(1, 2, true, [this](int x, int y) {
   blah;
   blah;
   blah;
}, 4, 5);

vs

auto fooProc = [this](int x, int y) {
   blah;
   blah;
   blah;
};

...

foo(1, 2, true, fooProc, 4, 5);

A czasami używam takiej lambdy po prostu jako funkcji lokalnej, żeby nie powiększać jej sztucznie zasięgu:

auto bar = [](int x, int y) {
   blah;
   blah;
   blah;
};

...

bar(1, 2);
0

U mnie wyglądało by to tak

void bar(int x, int y)
{
   blah;
   blah;
   blah;
}

void something()
{
    foo(1, 2, true, [this](int x, int y) { bar(x, y); }, 4, 5);
}

przy czym moje foo nigdy nie ma aż tylu argumentów.
Dodam jeszcze, że nie tylko robię tak, bo to jest moje widzimisię. Ma to też praktyczną zaletę.
Mogę użyć tego kodu w innym miejscu. Z nazwaną lambdą tak się nie da, bo jest widoczna tylko w ramach danej funkcji.
Na dodatek nie muszę się martwic r-value i move schematics, przy nazwanej lambda trzeba o tym myśleć.

Jak czytam kod napisany w takim stylu (a dużo go widzę):

void something(int a, int b)
{
     doA(a);
     doA(b);
     auto foo = [this](int x, int y) {
          bla1();
          bla2();
          bla3();
          ....
          bla200();
     };

     doB(b);
     if (a < b) {
         doB(a);
         foo(1, 3);
     }
     bar(1, 2, 3, 4, foo, 3);
}

to dostaje oczopląsu.

1 użytkowników online, w tym zalogowanych: 0, gości: 1