Перегрузка функции C++ в соответствии с возвращаемым значением
Мы все знаем, что вы можете перегружать функцию в соответствии с параметрами:
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 ответов:
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; };
Не то, чтобы я использовал это.
Вы должны указать компилятору, какую версию использовать. В 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"
Об этом стоило упомянуть, я думаю, даже если вы не хотели, чтобы решение.
Явно дифференцировать вызовы по фиктивному указателю
Вы можете добавить фиктивный параметр к каждой функции, тем самым заставляя компилятор выбирать правильные функции. Самый простой способ-отправить пустой фиктивный указатель типа, желаемого для возврата:
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++...
: - p
Если вы хотите сделать
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; }
Нет.
Вы не можете перегрузить возвращаемое значение, потому что вызывающий объект может сделать с ним все (или ничего). Рассмотрим:
mul(1, 2);
Возвращаемое значение просто выбрасывается, поэтому он никак не может выбрать перегрузку, основанную только на возвращаемом значении.
Используйте неявное преобразование в промежуточном классе.
class BadIdea { public: operator string() { return "silly"; } operator int() { return 15; } }; BadIdea mul(int, int)
У вас есть идея, хотя и ужасная идея.
Нельзя перегружать функцию только на основе возвращаемого значения.
Однако, хотя строго говоря это не перегруженная функция, вы можете вернуть из своей функции в результате экземпляр класса, который перегружает операторы преобразования.
Я предполагаю, что вы можете вернуть какой-то странный тип Foo, который просто захватывает параметры, а затем Foo имеет неявный оператор int и строку оператора, и это будет "работать", хотя на самом деле это не будет перегрузкой, а скорее неявным трюком преобразования.
Хммм, следующаястатья проекта кода , кажется, делает то, что вы ищете. Должно быть, магия ;)
Короткий и простой ответ-нет. В 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);
Насколько я знаю, вы не можете (хотя очень жаль...). В качестве обходного пути можно определить параметр " out " и перегрузить его.
Не в C++. То, что вы получите в приведенном выше примере, будет возвращенным значением, которое является int, приведенным к чему-то, что
string
может понять, скорее всего,char
. Это будет ASCII 18 или "device control 2".
Вы можете использовать решение функтора выше. C++ не поддерживает это для функций, за исключением const. Вы можете перегрузить на основе const.
Вы можете использовать шаблон, но тогда вам придется указать параметр шаблона при выполнении вызова.
Поместить его в другое пространство имен? Вот как я это сделаю. Не совсем перегрузка, а просто наличие двух методов с одинаковым именем, но с разной областью видимости (отсюда оператор разрешения :: scope).
Итак, stringnamespace:: mul и intnamespace:: mul. Может быть, это не совсем то, что вы просите, но это кажется единственным способом сделать это.