2013-02-12 5 views
8

Мне нужно найти способ перекомпоновки перегрузки возвращаемого типа функции в C++.C++ те же функциональные параметры с разными типами возврата

Я знаю, что нет прямого способа сделать это напрямую, но я надеюсь, что вокруг него будет какой-то из коробки. Мы создаем API для пользователей, работающих под ними, и они будут передавать строку данных, которая извлекает значение на основе строковой информации. Эти значения являются разными типами. В сущности, мы хотели бы сделать это:

int = RetrieveValue(dataString1); 
double = RetrieveValue(dataString2); 
// Obviously, since they don't know the type, they wouldn't use int =.... It would be: 
AnotherFunction(RetrieveValue(dataString1)); // param of type int 
AnotherFunction(RetrieveValue(dataString2)); // param of type double 

Но это не работает на C++ (очевидно). Прямо сейчас, мы с его настроить так, что они называют:

int = RetrieveValueInt(dataString1); 
double = RetrieveValueDouble(dataString2); 

Однако мы не хотим, чтобы они должны знать, что тип их строки данных.

К сожалению, нам не разрешено использовать внешние библиотеки, поэтому без использования Boost.

Есть ли способы обойти это?

Просто, чтобы уточнить, я понимаю, что C++ не может изначально сделать это. Но должен быть какой-то способ обойти это. Например, я думал о выполнении RetrieveValue (dataString1, GetType (dataString1)). Это ничего не исправляет, потому что GetType также может иметь только один тип возврата. Но мне нужно что-то подобное.

Я понимаю, что этот вопрос задавали раньше, но в другом смысле. Я не могу использовать какие-либо очевидные ответы. Мне нужно что-то совершенно готовое, чтобы оно мне было полезно, чего не было в ответах в другом вопросе.

+6

Как узнать, для какого типа назначить результат функции? –

+0

Возможный дубликат [Головоломка: перегрузка функции C++ в соответствии с возвращаемым значением] (http://stackoverflow.com/questions/226144/puzzle-overload-ac-function-according-to-the-return-value) –

+1

Будет ли вы показываете, как вы хотите, чтобы эта функция вызывалась? И int x = RetrieveValue (...) Не потому, что они должны указать int для x, который разбивает одно из ваших условий. Я думаю, что этот вопрос плохо продуман. –

ответ

19

Вы имеете, чтобы начать с этого:

template<typename T> 
T RetrieveValue(std::string key) 
{ 
    //get value and convert into T and return it 
} 

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

template<typename T> 
T RetrieveValue(std::string key) 
{ 
    //get value 
     std::string value = get_value(key, etc); 

     std::stringstream ss(value); 
     T convertedValue; 
     if (ss >> convertedValue) return convertedValue; 
     else throw std::runtime_error("conversion failed"); 
} 

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

int x = RetrieveValue<int>(key); 

Вы могли бы избежать упоминания int в два раза, если вы могли бы сделать это вместо:

Value RetrieveValue(std::string key) 
{ 
    //get value 
     std::string value = get_value(key, etc); 
     return { value }; 
} 

где Value реализуется как:

struct Value 
{ 
    std::string _value; 

    template<typename T> 
    operator T() const //implicitly convert into T 
    { 
     std::stringstream ss(_value); 
     T convertedValue; 
     if (ss >> convertedValue) return convertedValue; 
     else throw std::runtime_error("conversion failed"); 
    } 
} 

Тогда вы могли бы написать это:

int x = RetrieveValue(key1); 
double y = RetrieveValue(key2); 

который, который вы хотите, верно?

+0

И как подсказка для OP, выполните поиск «специализации шаблона». –

+1

Но они все равно должны будут указать тип в этом случае. Это невозможно вывести. Таким образом, вместо 'RetrieveValueInt', им придется писать' RetrieveValue ', и я не вижу в нем большой разницы, за исключением возможности использования последнего в целом. –

+0

@Benjamin Они все равно задают тип с 'int x =', неужели? – JBentley

0

К сожалению, нет никакого способа, чтобы перегрузить функцию возвратного типа см этот ответ Overloading by return type

+2

Я бы сказал« К счастью, ... ». –

+0

+1 К тому, да «К счастью» для здравого смысла, но неудачно, если по какой-то причине вы действительно не используете эту функцию. – Twiltie

+0

Это действительно удивительно полезная функция в Ada, но она работает только там из-за отсутствия неявных преобразований Ada. –

0
int a=itoa(retrieveValue(dataString)); 
double a=ftoa(retrieveValue(dataString)); 

возвращают строку.

1

Если вы знаете значение никогда не может быть чем-то вроде нуля или отрицательного, просто возвращает структуру удерживающей Int и дважды и обнулить один вам не нужно ...

Это дешевое и грязные, но легкий путь ...

struct MyStruct{ 
int myInt; 
double myDouble; 
}; 

MyStruct MyFunction(){ 
} 
+0

Или добавьте логическое или третье значение, указывающее, какое значение следует читать (int или double). Опять же, это не оптимально или элегантно, но супер прямолинейно ... Класс тоже будет работать, но не имеет реальной точки, если у вас просто есть int и double. – HackyStack

+0

Мы думали об этом, однако, мы не можем иметь пользователей иметь дело со структурой. –

+1

У вас есть 2 варианта: используйте класс или struct или pass params через ссылку, что означает отсутствие возвращаемого значения (или, по крайней мере, не int и double). Существует ужасный способ сделать это: вы можете вернуть строку, содержащую значение и тип, чтобы у вас были бы «int 1234» или «double 123.456», но тогда пользователи должны расшифровать ... Извините, но у вас нет много вариантов. – HackyStack

0

в качестве альтернативы решению шаблона, вы можете иметь функцию, возвращающую указатель или ссылку на класс, а затем создать подклассы этого класса содержат различные типы данных, которые вы» d хотел бы вернуться. RetrieveValue затем вернет ссылку на соответствующий подкласс.

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

Проблема в этом случае стала бы одним из способов управления памятью - выбор функции, которая распределяет возвращенный объект, и какая функция его удаляет, и когда таким образом мы избегаем утечек памяти.

0

Единственный разумный способ сделать это - переместить возвращаемое значение в параметры.

void retrieve_value(std::string s, double& p); 
void retrieve_value(std::string s, int& p); 
<...> 

double x; 
retrieve_value(data_string1, x); 

int y; 
retrieve_value(data_string2, y); 
+0

Ваш заказ объявленных параметров не соответствует порядку фактических параметров. –

+0

Недостатком этого подхода является то, что при использовании объектов они уже должны быть сконструированы, что может потребовать реализации конструкторов-заглушек –

1

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

int RetrieveValue(const std::string& s, const int&) { 
    return atoi(s.c_str()); 
} 
double RetrieveValue(const std::string& s, const double&) { 
    return atof(s.c_str()); 
} 

int i = RetrieveValue(dataString1, i); 
double d = RetrieveValue(dataString2, d); 
+0

. Я обнаружил, что использовал этот точный шаблон для «перегрузки» возвращаемого типа в своих функциях создания виджета графического интерфейса. – MarcD

-2

Поскольку вы использовали пример, который был на самом деле не то, что вы хотели, вы бросили все прочь немного.

Установки, которые у вас действительно есть (вызов функции с возвращаемым значением этой функции, тип которой непознаваем, не будет работать, поскольку вызовы функций разрешаются во время компиляции.

Тогда вы ограничены решением для выполнения. Я рекомендую шаблон посетителя, и вам придется существенно изменить свой дизайн, чтобы это изменение изменилось. На самом деле нет другого способа сделать это, что я вижу.

+0

Как не был мой пример, что я хотел? –

+0

@ JoshJohnson вы сказали, что пример в вашем вопросе не так, как он действительно будет использоваться, не так ли? –

+0

Он будет использоваться, как показано на рисунке. Это может быть более непосредственно использовано внутри другого вызова функции, однако это не показало бы различие, которое необходимо было бы четко. Пример, который я дал, по-прежнему действителен для функции. Вы сказали, что это не совсем то, что я хотел. Это неправда. Я хочу этого ... –

1

Если данные являются константами времени компиляции (как сказано в ответе на мой комментарий), вы можете использовать магию шаблонов для выполнения задания. Еще более простой вариант - не использовать строки вообще, а некоторые типы данных, которые позволяют вам перегружать аргумент.

struct retrieve_int {} as_int; 
struct retrieve_double {} as_double; 

int RetrieveValue(retrieve_int) { return 3; } 
double RetrieveValue(retrieve_double) { return 7.0; } 

auto x = RetrieveValue(as_int); // x is int 
auto y = RetrieveValue(as_double); // y is double 
Смежные вопросы