Przeciążanie zwracanego typu

0

Załóżmy, że chcę móc łatwo dokonać konwersji typów zmiennych pomiędzy "string", "int" i "char[]". Sama treść tych funkcji nie jest teraz istotna, chodzi o możliwości implementacji i użycia. W takiej sytuacji jest 6 różnych zmian typów.

Najprościej zrobić tak:

char[] ToChar(int N)
{
    // Zamiana int na char[]
}
char[] ToChar(string N)
{
    // Zamiana string na char
}
string ToStr(int N)
{
    // Zamiana int na string
}
string ToStr(char[] C)
{
    // Zamiana char[] na string
}
int ToInt(string S)
{
    // Zamiana string na int
}
int ToInt(char[] S)
{
    // Zamiana char[] na int
}

Słyszałem, że w C++ da się zrobić jedną funkcję, która konwertuje typy "z każdego na każdy", przy czym w samej funkcji muszą być obsłużone wszystkie przejścia. Wyobrażam sobie, że to wygląda mniej więcej tak:

char[] Conv(int N)
{
    // Zamiana int na char[]
}
char[] Conv(string N)
{
    // Zamiana string na char
}
string Conv(int N)
{
    // Zamiana int na string
}
string Conv(char[] C)
{
    // Zamiana char[] na string
}
int Conv(string S)
{
    // Zamiana string na int
}
int Conv(char[] S)
{
    // Zamiana char[] na int
}

Wtedy w każdym przypadku użycia funkcji Conv kompilator wychwyciłby, jaki typ ma podany parametr i jakiego typu jest wymagany wynik działania funkcji. Na przykład coś takiego:

int liczba = 123;
string napis = Conv(liczba);

W tym przykładzie kompilator wybrałby funkcję "int Conv(string S)".

Jak spróbowałem czegoś takiego, to program nie chce się skompilować pokazując błąd, że są dwie wersje przeciążonej funkcji różniące się tylko zwracanym typem. Używam QtCreator.

Czy rzeczywiście da się zrobić funkcję, która tak samo się nazywa, przyjmuje takie same typy argumentów, ale zwraca różne typy wyniku? Jeżeli tak, to w jaki sposób?

0

W tym przykładzie kompilator wybrałby funkcję "int Conv(string S)".

Nie, wybrałby std::string Conv(int). Przeciążyć funkcje możesz tylko dla różnych typów argumentów, typ zwracany nie ma znaczenia, więc tylko na jego podstawie nie można niczego przeciążyć.

Tak btw, to to już wszystko jest: http://en.cppreference.com/w/cpp/string/basic_string sekcja "Numeric conversions". A "konwertowanie" std::string do const char * jest bez sensu, do tego jest metoda .c_str(). Jeżeli chcesz się bawić w char * to odpuść sobie, przecież właśnie po to jest std::string.

0

http://ideone.com/q3Yc72 Z typami numerycznymi powinno dzialac, innych w zaden sposob nie sprawdzalem.

0
char[] Conv(int N)
{
// Zamiana int na char[]
}
string Conv(int N)
{
// Zamiana int na string
}

chyba w każdym języku programowania pozwalającym na przeciążanie funkcji, funkcje muszą się różnić ilością lub typem parametrów, a nie zwracanym typem.
obie podane funkcje przyjmują jeden parametr typu int, więc coś takiego nie przejdzie.

1

Z tym, że możesz to w pewnym sensie "ominąć - zwracając zawsze jeden typ: klasę pośrednią. Taka klasa sama z sobie "nic by potrafiła", ale miałaby zdefiniowane operatory automatycznej konwersji. Zależnie od kontekstu ruszałby odpowiedni operator konwersji. To by wyglądało jakoś tak:

#include <iostream>
#include <string>
#include <cstdlib>
#include <cstdio>

using namespace std;

// fuj, klasa "domyślna", jeśli żadna specjalizacja nie pasuje

template<class T>
class conversion_target {

    const T& value;

    public:
    conversion_target(const T& _value) : value(_value) { }

    operator const char*() {
        return "<not supported>";
    }

    operator string() {
        return "<not supported>";
    }

    operator int() {
        return -1;
    }

    operator float() {
        return -1;
    }

};

// konwersje z tekstu (ze stałej dosłownie wpisanej, tzn literału)

template<int N>
class conversion_target<char[N]> {
    const char* value;

    public:
    conversion_target(const char*_value ) : value(_value) { }

    operator const char*() {
        return value;
    }

    operator string() {
        return value;
    }

    operator int() {
        return atoi(value);
    }

    operator float() {
        return atof(value);
    }
};

// konwersje z tekstu (z const char*)

template<>
class conversion_target<const char*> {
    const char* value;

    public:
    conversion_target(const char* _value) : value(_value) { }

    operator const char*() {
        return value;
    }

    operator string() {
        return value;
    }

    operator int() {
        return atoi(value);
    }

    operator float() {
        return atof(value);
    }
};

// konwersje z tekstu (z char*)

template<>
class conversion_target<char*> {
    char* const value;

    public:
    conversion_target(char * const _value) : value(_value) { }

    operator const char*() {
        return value;
    }

    operator string() {
        return value;
    }

    operator int() {
        return atoi(value);
    }

    operator float() {
        return atof(value);
    }
};

// konwersje z liczby (int)

template<>
class conversion_target<int> {
    const int value;
    char buffer[32];

    public:
    conversion_target(int _value) : value(_value) { }

    operator const char*() {
        sprintf(buffer,"%d", value);
        return buffer;
    }

    operator string() {
        sprintf(buffer,"%d", value);
        return buffer;
    }

    operator int() {
        return value;
    }

    operator float() {
        return value;
    }
};

// woooo - super genialna metoda konwertująca (lol), która w zasadzie nic mądrego
// nie robi. Po prostu na podstawie argumentu odpowiedni obiekt pośredni utworzy

template<class T>
conversion_target<T> convert(const T& value) {
    return conversion_target<T>(value);
}

// czy działa to to w ogóle ?

int main() {
    const char* input1 = "17";
    char* input2 = "18";
    char input3[] = "19";

    int output1 = convert(input1);
    int output2 = convert(input2);
    int output3 = convert(input3);
    int output4 = convert( "20" );

    string output5 = convert(21);

    cout << output1 << endl;
    cout << output2 << endl;
    cout << output3 << endl;
    cout << output4 << endl;
    cout << output5 << endl;

    //  nie napisaliśmy klasy, która konwertuje z liczby (double)...
    string output6 = convert( 17.7 );
    // i faktycznie: klasa "domyślna" ruszyła
    cout << output6 << endl;

}

Uwaga - od kupy czasu nie piszę w C++ na poważnie, wielu rzeczy nie pamiętam. Metody w klasie "domyślnej" da się napisać jakoś tak, żeby błąd kompilacji w linijce output6 wyskoczył. Lepiej by tak było, zamiast "głupi" wynik zwracać - ale nie pamiętam już totalnie jak :(

0

Wszystko fajnie, ale jaki jest sens pisania konwersji int na float, skoro ona zajdzie sama kiedy trzeba? Jedyne co ma sens to std::string, ale do tego są funkcje w standardzie.

Poza faktem, że jest to niepotrzebne to jako przykład jest dobre. :-)

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