Перегрузить функцию C ++ в соответствии с возвращаемым значением - PullRequest
33 голосов
/ 22 октября 2008

Мы все знаем, что вы можете перегрузить функцию в соответствии с параметрами:

int mul(int i, int j) { return i*j; }
std::string mul(char c, int n) { return std::string(n, c); } 

Можете ли вы перегрузить функцию в соответствии с возвращаемым значением? Определите функцию, которая возвращает разные вещи в зависимости от того, как используется возвращаемое значение:

int n = mul(6, 3); // n = 18
std::string s = mul(6, 3); // s = "666"
// Note that both invocations take the exact same parameters (same types)

Можно предположить, что первый параметр находится в диапазоне 0-9, нет необходимости проверять ввод или обрабатывать ошибки.

Ответы [ 17 ]

50 голосов
/ 23 октября 2008

Вы должны указать компилятору, какую версию использовать. В C ++ вы можете сделать это тремя способами.

Явно дифференцируйте звонки, набрав

Вы несколько обманули, потому что отправили целое число в функцию, ожидающую символ, и ошибочно отправили число шесть, когда значение символа '6' не 6, а 54 (в ASCII):

std::string mul(char c, int n) { return std::string(n, c); }

std::string s = mul(6, 3); // s = "666"

Правильное решение было бы, конечно,

std::string s = mul(static_cast<char>(54), 3); // s = "666"

Думаю, это стоило упомянуть, даже если вы не хотели решения.

Явно дифференцирует вызовы по фиктивному указателю

Вы можете добавить фиктивный параметр для каждой функции, заставляя компилятор выбирать правильные функции. Самый простой способ - отправить фиктивный указатель NULL типа, требуемого для возврата:

int mul(int *, int i, int j) { return i*j; }
std::string mul(std::string *, char c, int n) { return std::string(n, c); }

Который можно использовать с кодом:

int n = mul((int *) NULL, 6, 3); // n = 18
std::string s = mul((std::string *) NULL, 54, 3); // s = "666"

Явно дифференцируйте вызовы путем шаблонирования возвращаемого значения

С помощью этого решения мы создаем «фиктивную» функцию с кодом, который не будет компилироваться, если будет создан экземпляр:

template<typename T>
T mul(int i, int j)
{
   // If you get a compile error, it's because you did not use
   // one of the authorized template specializations
   const int k = 25 ; k = 36 ;
}

Вы заметите, что эта функция не будет компилироваться, что хорошо, потому что мы хотим использовать только некоторые ограниченные функции через специализацию шаблона:

template<>
int mul<int>(int i, int j)
{
   return i * j ;
}

template<>
std::string mul<std::string>(int i, int j)
{
   return std::string(j, static_cast<char>(i)) ;
}

Таким образом, будет скомпилирован следующий код:

int n = mul<int>(6, 3); // n = 18
std::string s = mul<std::string>(54, 3); // s = "666"

Но этот не будет:

short n2 = mul<short>(6, 3); // error: assignment of read-only variable ‘k’

Явно дифференцируйте вызовы путем шаблонирования возвращаемого значения, 2

Эй, ты тоже обманул!

Правильно, я использовал одни и те же параметры для двух "перегруженных" функций. Но вы начали обманывать (см. Выше) ...

^ _ ^

Если серьезно, если вам нужны разные параметры, вам нужно написать больше кода, а затем явно использовать правильные типы при вызове функций, чтобы избежать неоднозначностей:

// For "int, int" calls
template<typename T>
T mul(int i, int j)
{
   // If you get a compile error, it's because you did not use
   // one of the authorized template specializations
   const int k = 25 ; k = 36 ;
}

template<>
int mul<int>(int i, int j)
{
   return i * j ;
}

// For "char, int" calls
template<typename T>
T mul(char i, int j)
{
   // If you get a compile error, it's because you did not use
   // one of the authorized template specializations
   const int k = 25 ; k = 36 ;
}

template<>
std::string mul<std::string>(char i, int j)
{
   return std::string(j, (char) i) ;
}

И этот код будет использоваться как таковой:

int n = mul<int>(6, 3); // n = 18
std::string s = mul<std::string>('6', 3); // s = "666"

И следующая строка:

short n2 = mul<short>(6, 3); // n = 18

Все равно не скомпилируется.

Заключение

Я люблю C ++ ...

: - р

41 голосов
/ 22 октября 2008
class mul
{
public:
    mul(int p1, int p2)
    {
        param1 = p1;
        param2 = p2;
    }
    operator int ()
    {
        return param1 * param2;
    }

    operator std::string ()
    {
        return std::string(param2, param1 + '0');
    }

private:
    int param1;
    int param2;
};

Не то чтобы я использовал это.

23 голосов
/ 10 января 2009

Если вы хотите сделать mul реальной функцией вместо класса, вы можете просто использовать промежуточный класс:

class StringOrInt
{
public:
    StringOrInt(int p1, int p2)
    {
        param1 = p1;
        param2 = p2;
    }
    operator int ()
    {
        return param1 * param2;
    }

    operator std::string ()
    {
        return std::string(param2, param1 + '0');
    }

private:
    int param1;
    int param2;
};

StringOrInt mul(int p1, int p2)
{
    return StringOrInt(p1, p2);
}

Это позволяет делать такие вещи, как передача mul как функции в алгоритмы std:

int main(int argc, char* argv[])
{
    vector<int> x;
    x.push_back(3);
    x.push_back(4);
    x.push_back(5);
    x.push_back(6);

    vector<int> intDest(x.size());
    transform(x.begin(), x.end(), intDest.begin(), bind1st(ptr_fun(&mul), 5));
    // print 15 20 25 30
    for (vector<int>::const_iterator i = intDest.begin(); i != intDest.end(); ++i)
        cout << *i << " ";
    cout << endl;

    vector<string> stringDest(x.size());
    transform(x.begin(), x.end(), stringDest.begin(), bind1st(ptr_fun(&mul), 5));
    // print 555 5555 55555 555555
    for (vector<string>::const_iterator i = stringDest.begin(); i != stringDest.end(); ++i)
        cout << *i << " ";
    cout << endl;

    return 0;
}
20 голосов
/ 22 октября 2008

номер

Вы не можете перегрузить возвращаемым значением, потому что вызывающая сторона может делать с ним что угодно (или ничего). Рассмотрим:

mul(1, 2);

Возвращаемое значение просто выбрасывается, поэтому нет способа выбрать перегрузку только на основе возвращаемого значения.

8 голосов
/ 22 октября 2008

Использование неявного преобразования в промежуточном классе.

class BadIdea
{
  public:
    operator string() { return "silly"; }
    operator int() { return 15; }
};

BadIdea mul(int, int)

Вы поняли идею, хотя ужасная идея.

4 голосов
/ 22 октября 2008

Пусть mul будет классом, mul (x, y) его конструктором и перегрузит некоторые операторы приведения.

3 голосов
/ 22 октября 2008

Вы не можете перегрузить функцию только на основе возвращаемого значения.

Однако, строго говоря, это не перегруженная функция, в результате вы можете вернуть из своей функции экземпляр класса, который перегружает операторы преобразования.

2 голосов
/ 22 октября 2008

Полагаю, вы могли бы заставить его возвращать какой-то странный тип Foo, который просто захватывает параметры, и тогда у Foo есть неявный оператор int и строка оператора, и он будет «работать», хотя на самом деле это не будет перегрузкой, скорее неявным конверсионный трюк.

1 голос
/ 22 октября 2008

Хммм, следующая кодовая статья проекта , кажется, делает то, что вам нужно. Должно быть волшебство;)

1 голос
/ 20 июля 2011

Коротко и просто, ответ НЕТ. В C ++ требования следующие:

1: имена функций ДОЛЖНЫ быть одинаковыми
2: набор аргументов ДОЛЖЕН отличаться
* Тип возврата может быть одинаковым или различным

//This is not valid
    int foo();
    float foo();

    typedef int Int;

    int foo(int j);
    int foo(Int j);

//Valid:
   int foo(int j);
   char* foo(char * s);
   int foo(int j, int k);
   float foo(int j, float k);
   float foo(float j, float k);
...