Изменение типа возвращаемого значения функции без специализации шаблона. C ++

StackOverflow https://stackoverflow.com/questions/1010539

  •  06-07-2019
  •  | 
  •  

Вопрос

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

Я хочу создать функцию, которая анализирует переменную типа int, bool или float из строки. Например ...

Int value = parse("37");
Float value = parse("3.14");
Bool value = parse("true");

Я понимаю, что если я сделаю эту функцию шаблоном, то тип переменной должен быть определен из списка аргументов, который всегда будет строкой. Есть ли другой способ сделать это с C ++?

Это было полезно?

Решение

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

struct proxy {
    string str;
    proxy(string const &str):str(str) { }
    template<typename T> operator T() { 
        return boost::lexical_cast<T>(str); 
    }
};

proxy parse(string const &str) { return proxy(str); }

Теперь вам просто нужно сделать

float a = parse("3.1");

И это должно хорошо работать. Кстати, вы можете просто использовать класс напрямую. Я рекомендую переименовать его в translation_proxy , чтобы указать на тот факт, что это просто прокси для происходящего преобразования, но само преобразование не выполняется

struct conversion_proxy {
    string str;
    conversion_proxy(string const &str):str(str) { }
    template<typename T> operator T() { 
        return boost::lexical_cast<T>(str); 
    }
};

float a = conversion_proxy("3.1"); 

Другие советы

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

template<typename T> T parse(const string& str) { /* do stuff for other types */ }
template<> int parse<int>(const string& str) { /* do stuff for ints */ }
template<> double parse<double>(const string& str) { /* do stuff for doubles */ }
template<> bool parse<bool>(const string& str) { /* do stuff for bools */ }
// etc.

А затем вызвать как

int value = parse<int>("37");
double value = parse<double>("3.14");
bool value = parse<bool>("true");

Если вы уже знали об этом, просто проигнорируйте этот ответ, но из вашего вопроса не ясно, знаете ли вы, что это возможно.

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

Кстати, вы можете сделать это довольно обобщенно с помощью одной функции, подобной этой (при условии, что синтаксический анализ - это то, что вы действительно хотите сделать):

#include <sstream>
template<typename T> T parse(const string& str) 
{
  T t;
  std::istringstream sstr(str);
  sstr >> t;
  return t;
}

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

Вы можете передать свой выходной аргумент в виде указателя или ссылки.

Вот так:

template<class T> void parse(const std::string &input, T& output);

Тогда напишите такой код:

double d; parse(input, d);
int i; parse(input, i);

должно работать.

Тем не менее, ваш код выглядит идеально подходящим для std :: istringstream, который просто будет:

istringstream is(input);
input >> d;

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

Тогда это может быть как:

istringstring is(input);
input >> LineExtracter(x, y, d);

Я бы согласился с Литбом, который был немного быстрее меня. Используйте операторы приведения.

#include <iostream>
#include <string>
#include <sstream>

class Convertible
{
public:
    int m_Integer;
    bool m_Bool;
    double m_Double;

    Convertible() : m_Integer(0), m_Bool(false), m_Double(0.0) {};

    operator int() const
    {
        return m_Integer;
    }
    operator bool() const
    {
        return m_Bool;
    }
    operator double() const
    {
        return m_Double;
    }
};

Convertible parse(std::string data)
{
    Convertible l_result;

    std::istringstream converter(data);
    converter >> l_result.m_Integer;

    std::istringstream converter2(data);
    converter2 >> l_result.m_Bool;

    std::istringstream converter3(data);
    converter3 >> l_result.m_Double;

    return l_result;
}

void main()
{
    int l_convertedInt = parse("2");
    bool l_convertedBool = parse("true");
    double l_convertedDouble = parse("3.14");

    std::cout << "Converted '2' to " << l_convertedInt << std::endl;
    std::cout << "Converted 'true' to " << l_convertedBool << std::endl;
    std::cout << "Converted '3.14' to " << l_convertedDouble << std::endl;
}

К сожалению, это невозможно. В C ++ невозможно перегрузить функцию, основываясь на ее возвращаемом значении. Вы должны иметь 3 функции: ParseInt, ParseFloat и ParseBool, либо использовать шаблон функции.

Вы можете вернуть void *, а затем при необходимости привести результат.

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

Нет, этот тип поведения невозможен в C ++. Чтобы это было допустимо, необходимо иметь возможность определять функции с одинаковыми именами в одной и той же области видимости, которые отличаются только типом возвращаемого значения. Это недопустимо в C ++.

C ++ может выполнять некоторую специализацию возвращаемого типа, например ковариантные возвращаемые типы в переопределенных виртуальных функциях. Но это не поддерживает то, что вы ищете.

Вот моя адаптация ответа @Tyler McHenry для моей ситуации, когда аргумент parse () - это тип, отличный от строки.

Обратите внимание, что мне пришлось ввести специализацию шаблонов , чтобы избежать предупреждения о преобразовании типов ( float в int ).

(см. также демо-версию .)

#include <iostream>

struct MyUnion
{
public:
  union {
    bool bool_value;
    int int_value;
    float float_value;
  };
};

template<typename T> T parse(const MyUnion& h)
{
  T t;

  if (typeid(T) == typeid(bool)) {
    t = h.bool_value;
  } else if (typeid(T) == typeid(int)) {
    t = h.int_value;
  } else if (typeid(T) == typeid(float)) {
    // t = h.float_value; // see **Warning** below; use float specialization instead
  }

  return t;
}

// 'float' template specialization to avoid conversion warning.
template<> float parse(const MyUnion& h)
{
  return h.float_value;
}

int main()
{
  MyUnion mu1; mu1.bool_value = true;
  MyUnion mu2; mu2.int_value = 42;
  MyUnion mu3; mu3.float_value = 3.14159;

  std::cout << "As bool: "  << parse<bool>(mu1)  << std::endl;
  std::cout << "As int: "   << parse<int>(mu2)   << std::endl;
  std::cout << "As float: " << parse<float>(mu3) << std::endl;
}

// **Warning**
// In function 'T parse(const Heterogeneous&) [with T = int]':
// Line 22: warning: converting to 'int' from 'const float'
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top