Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

Oberon

Treue Seele

  • »Oberon« ist der Autor dieses Themas

Beiträge: 181

Wohnort: Österreich

Beruf: Student

  • Private Nachricht senden

1

01.07.2013, 12:38

[C++] Quizfrage: Overload Resolution

Ich hoffe das Thema ist hier angemessen, da ich ja nicht direkt um Hilfe bitte, sondern, wie der Titel schon sagt, eine Quizfrage für euch habe (im Stile von Herb Sutters Guru of the Week (siehe auch)). Die Frage stellte sich bei mir tatsächlich, ist also nicht konstruiert (nur simplifiziert natürlich). Also:

Gegeben ist folgender Programmcode:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream> // std::cout
#include <cstddef>  // std::size_t

template <std::size_t N>
void foo(char const (&)[N])
{
    std::cout << "foo<" << N << ">(char const (&)["<< N << "])\n";
}

void foo(char const*)
{
    std::cout << "foo(char const*)\n";
}


int main()
{
    foo("bar");
}

Beantworte dazu die folgenden Fragen:
  1. Was bedeutet die Deklaration der ersten Überladung von foo() bzw. welche Argumente nimmt sie entgegen?
  2. Was gibt das Programm aus (d.h. welche Überladung von foo()wird aufgerufen)?
  3. Warum ist das so?
  4. Was muss man am Quellcode verändern um für diesen Aufruf die andere Überladung zu erreichen (ohne irgendein foo() zu entfernen oder inkompatibel zu Argumenten zu machen, zu denen sie vorher kompatibel war)? (Vorsicht bei GCC ≤ 4.7.x!) Begründe!
Bei der letzten Frage (bzw. bei der Begründung von deren Antwort) bin ich mir ehrlich gesagt selbst nicht ganz sicher, da ich nicht so viel Erfahrung im Lesen des Standards habe und würde mich besonders freuen mit euch darüber zu diskutieren.

Tipp: Wer es nicht schon weiß, dem könnte (wie mir) der C++ Standard helfen. Wie man den (manche Entwurfsversionen (hier völlig ausreichend) auch kostenlos) bekommt steht z.B. unter http://isocpp.org/std/the-standard.

Ich werde die Frage wohl nicht wie bei GotW erst nach einer Woche auflösen, sondern sobald keine Antworten mehr kommen (frühestens am Mittwochabend).

SPOILERWARNUNG: Ihr könnt einfach mit Posts hier im Thread antworten. Das heißt aber, dass ihr beim weiter runterscrollen unweigerlich auf Antworten die Auflösung stoßen werdet. ;) Wer das nicht will sollte hier zu scrollen aufhören und den Antwort-Button rechts oben benutzen (und beim Antworten wieder mit dem runterscrollen aufpassen).

Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von »Oberon« (03.07.2013, 22:32) aus folgendem Grund: Spoilerwarnung eingefügt.


dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

2

01.07.2013, 13:00

In welcher Form sollen wir denn antworten? ;)

Oberon

Treue Seele

  • »Oberon« ist der Autor dieses Themas

Beiträge: 181

Wohnort: Österreich

Beruf: Student

  • Private Nachricht senden

3

01.07.2013, 13:05

Gute Frage. Ich denke, ihr könnt ganz normal hier posten, ich werde oben eine Warnung einfügen, dass wer sich nicht spoilern will, nicht runterscrollen soll. Es gibt ja auch oben rechts einen Antwort-Button.

EDIT: "Sobald keine Antworten mehr kommen (frühestens am Mittwochabend)" wird aufgelöst: das ist wie es scheint jetzt. Schade, dass keine Antworten kamen. Ich werde jetzt trotzdem mal sogleich Auflösen, vielleicht interessiert die Antwort oder die verbleibende Unklarheit ja jemand.

Dieser Beitrag wurde bereits 4 mal editiert, zuletzt von »Oberon« (04.07.2013, 10:10)


Oberon

Treue Seele

  • »Oberon« ist der Autor dieses Themas

Beiträge: 181

Wohnort: Österreich

Beruf: Student

  • Private Nachricht senden

4

04.07.2013, 10:11

Auflösung

  1. Was bedeutet die Deklaration der ersten Überladung von foo() bzw. welche Argumente nimmt sie entgegen?

    Diese Frage war relativ einfach zu Beantworten: char const (&)[N] ist eine Referenz auf ein konstantes char-Array der Größe N (wobei N ein nicht-Typ Template-Parameter vom Typ std::size_t ist). foo nimmt also solche char Arrays per Referenz entgegen und ist hauptsächlich für char-Literale wie "bar" gedacht.

  2. Was gibt das Programm aus (d.h. welche Überladung von foo()wird aufgerufen)?

    Mittels Compiler ist diese Frage ebenfalls schnell gelöst. Kompiliert und ausgeführt erscheint foo(char const*) in der Konsole.

  3. Warum ist das so?

    Das ist schon etwas schwieriger zu beantworten. Intuitiv "weiß" man ja, dass die am besten passende Überladung aufgerufen wird, was bei int vs. std::string auch ausreichende Expertise ist, aber hier leider nicht genügt. Man könnte ja denken (ich dachte das auch ^^), dass das mit N=4 (wg. Null-Terminator) instantiierte Funktionstemplate aufgerufen wird, da der Parameter ja restriktiver ist (alles, was das Template entgegennimmt, geht auch mit der const char*-Überladung, aber nicht umgekehrt).
    Denkste! Ausschlaggebend für die Wahl zur "best viable function", wie der Standard das nennt, ist die "best implicit conversion sequence" von Argument zu Parameter (pro Parameter/Argument-Paar). Hat eine Überladung einen Parameter, für den diese implizite Konvertierungssequenz besser ist, als für alle anderen, und sonst nur Parameter, die mindestens gleich gut sind, wie die aller anderen Überladungen (oder es gibt nur einen Parameter), wird sie aufgerufen. Ansonsten ist der Aufruf mehrdeutig. Es gilt also herauszufinden ob char const (&)[4] oder char const* für "bar" die bessere implizite Konvertierungssequenz bietet, wobei "bar" den Typ char const[4] hat. Wir sehen: char const[4] -> char const (&)[4] benötigt keinerlei Konvertierung und char const[4] -> char const* ist eine Array-to-pointer Konvertierung. Stellt sich raus, dass beide den selben Rang "Exact Match" haben. Wenn die erste Überladung keine Template-Instantiierung wäre, wäre der Aufruf tatsächlich mehrdeutig (nachprüfbar durch entfernen der template ... Zeile und ersetzen von N durch 4). Da in so einem Fall Nicht-Templates Vorrang vor Templates haben, wird hier jedoch die const char* Überladung gewählt. Wer es nachlesen will sei auf Unterabschnitt 13.3.3 "Best viable function" des C++-Standards verwiesen.

  4. Was muss man am Quellcode verändern um für diesen Aufruf die andere Überladung zu erreichen (ohne irgendein foo() zu entfernen oder inkompatibel zu Argumenten zu machen, zu denen sie vorher kompatibel war)? (Vorsicht bei GCC ≤ 4.7.x!) Begründe!

    Eine Möglichkeit wäre, auch die const char* Überladung zum Template zu machen. Dann ist bei ansonsten gleich guten Überladungen diejenige besser, die mehr spezialisiert als die andere ist "according to the partial ordering rules described in 14.5.6.2". Erklärungen die für normalsterbliche lesbar sind gibt es z.B. hier. Obwohl diese Erklärung wegen des Nicht-Typ Parameters N etwas schwierig nachzuvollziehen ist, müsste dann nach meiner Intuition folgender Ersatz für die const char* Überladung funktionieren:

    C-/C++-Quelltext

    1
    2
    3
    4
    5
    
    template <typename T>
    void foo(T const*)
    {
        std::cout << "foo(char const*)";
    }

    GCC ≤ 4.7.x gibt mir recht, Clang, MSVC und GCC 4.8 (außer MSVC mit Online-Compilern getestet) aber nicht, und ich denke somit auch der Standard nicht. Wir bekommen bei MSVC folgenden Compilerfehler:

    Quellcode

    1
    2
    3
    4
    5
    6
    7
    8
    
    arrayoverload-ambigous.cpp(18) : error C2668: 'foo' : ambiguous call to overloaded function
            arrayoverload-ambigous.cpp(11): could be 'void foo<char>(const T *)'
            with
            [
                T=char
            ]
            arrayoverload-ambigous.cpp(5): or       'void foo<4>(const char (&)[4])'
            while trying to match the argument list '(const char [4])'

    Eine Lösung besteht darin, T const* durch T zu ersetzen. Warum? Ich weiß es nicht, genauso wie ich nicht weiß warum ersteres nicht funktioniert. Ich bin aber an Antworten höchst interessiert. ;)
    Auch wenn meine Ausführungen unverständlich für euch sind, bitte ich um Rückmeldungen und werde versuchen mein Bestes zu tun um Verständlichkeit herzustellen.

Als Referenz hier alle erwähnten Ansätze in vollständig kompilierbar (aber tw. nach dem letzten Test leicht verändert): http://gist.github.com/Oberon00/5922937.

Werbeanzeige