2017-10-10 2 views
2

Ich stoße zufällig auf dieses Problem.Warum funktioniert Postfix fehlgeschlagen und Präfix funktioniert gut, wenn ein Iterator als Argument übergeben und an der Endposition rekursiv?

Ich habe gedacht, dass Google es sicher lösen kann, aber nachdem ich mehrere Schlüsselwörter durchsucht habe, kann ich noch keine Antworten finden, die mich sehr verwirrt haben.

Wenn ich Präfix an Schwanz Position verwenden, Codes funktioniert:

template<class ContinerIterator, class F> 
constexpr auto fun(ContinerIterator IteratorBegin, ContinerIterator IteratorEnd, F f) 
{ 
    switch (IteratorBegin == IteratorEnd) 
    { 
    case true: return; 
    case false: f(*IteratorBegin); 
    } 
    return fun(++IteratorBegin, IteratorEnd, f); 
} 
int main() 
{ 
    std::vector<int> a = { 1, 2, 3, 4 }; 
    fun(std::begin(a), std::end(a), [](auto &a)->auto{a *= 2; }); 
    for (auto v : a) 
    { 
     std::cout << v << std::endl; 
    } 
    return 0; 
} 

Drücken Sie eine beliebige Taste, um fortzufahren . . .


Howerer, wenn ich postfix verwenden, kommt IteratorBegin Nerven iteratorEnd und geht weit und weit weg, so segmentfault.

template<class ContinerIterator, class F> 
constexpr auto fun(ContinerIterator IteratorBegin, ContinerIterator IteratorEnd, F f) 
{ 
    switch (IteratorBegin == IteratorEnd) 
    { 
    case true: return; 
    case false: f(*IteratorBegin); 
    } 
    return fun(IteratorBegin++, IteratorEnd, f); 
} 
void test() 
{ 

} 
int main() 
{ 
    std::vector<int> a = { 1, 2, 3, 4 }; 
    fun(std::begin(a), std::end(a), [](auto &a)->auto{a *= 2; }); 
    for (auto v : a) 
    { 
     std::cout << v << std::endl; 
    } 
    return 0; 
} 

Ich habe versucht auf MSVC, G ++, Clang, alles scheitert. Hier ist Fehlerliste des gcc:

Segmentation fault (core dumped)

Hier Clang ist:

Fehler aufgetreten ist (Timeout). Versuchen Sie es später noch einmal.

+2

Es klingt wie Sie lernen müssen, können, wie ein Debugger zu verwenden, durch den Code zu treten. Mit einem guten Debugger können Sie Ihr Programm Zeile für Zeile ausführen und sehen, wo es von dem, was Sie erwarten, abweicht. Dies ist ein essentielles Werkzeug, wenn Sie programmieren wollen. Weiterführende Literatur: [Wie kleine Programme zu debuggen] (http://ericlippert.com/2014/03/05/how-to-debug-small-programs/) – NathanOliver

+0

Wenn Postfix nach der üblichen Semantik implementiert wird, sollte es sein offensichtlich. – StoryTeller

+0

Es ist ungewöhnlich, eine switch-Anweisung auf diese Weise zu sehen. Ich würde einfach einen normalen alten if/else dafür benutzen. – templatetypedef

Antwort

3

Diese Aussage

return fun(IteratorBegin++, IteratorEnd, f); 

mit einigen Ausnahmen können wie

fun(IteratorBegin, IteratorEnd, f); 
++IteratorBegin; 
return; 

So ist die Funktion mit dem gleichen Wert von IteratorBegin aufgerufen wird immer in Betracht gezogen werden.

Aus dem C++ Standard (5.2.6 Inkrement- und Dekrement)

1 Der Wert eines Ausdrucks ++ Postfix ist der Wert des Operanden. [ Hinweis: der erhaltene Wert ist eine Kopie des ursprünglichen Wertes -Ende note ] ...

Betrachten Sie das folgende einfache Programm

#include <iostream> 

void f(int x) 
{ 
    std::cout << "Inside f(x): x = " << x << std::endl; 
} 

int main() 
{ 
    int x = 0; 

    std::cout << "Before f(x): x = " << x << std::endl; 
    f(x++); 
    std::cout << "After f(x): x = " << x << std::endl; 

    return 0; 
} 

Sein Ausgang ist

Before f(x): x = 0 
Inside f(x): x = 0 
After f(x): x = 1 

Auch wird es nützlich sein, das folgende einfache Programm

zu betrachten
#include <iostream> 

int x = 0; 

void f(int x) 
{ 
    std::cout << "Local (parameter) x = " << x << std::endl; 
    std::cout << "Global variable ::x = " << ::x << std::endl; 
} 

int main() 
{ 
    f(x++); 

    return 0; 
} 

Sein Ausgang ist

Local (parameter) x = 0 
Global variable ::x = 1 
3

Die Vorsilbe Fall:

return fun(++IteratorBegin, IteratorEnd, f); 

sagt ersten Inkrement IteratorBegin nach dem anderen, und rufen dann die Funktion fun. Danach zurück.

Auf der anderen Seite, der Postfix Fall:

return fun(IteratorBegin++, IteratorEnd, f); 

sagt, rufen Sie zunächst fun(), erhöhen Sie dann den Iterator, und dann zurück.

Das bedeutet, dass fun() immer mit dem nicht inkrementierten Iterator aufgerufen wird.

3

Wenn Sie Postfix-Inkrement im Tail-Aufruf verwenden, erhält der rekursive Aufruf nicht den inkrementierten Wert des Iterators. Er ruft den Wert des Iterators ab, bevor das Inkrement angewendet wird. Daher ist die Rekursion unendlich. Das verursacht Stapelüberlauf.

Verwandte Themen