تغيير نوع الإرجاع للدالة دون تخصص القالب. 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");

ويجب أن تعمل بشكل جيد. بالمناسبة ، يمكنك فقط استخدام الفصل مباشرة. أوصي بإعادة تسميته إلى conversion_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);

أتفق مع Litb الذي كان أسرع مني. استخدم مشغلي الصب.

#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 ، أو استخدام قالب وظيفة.

يمكنك إعادة باطلة* ثم يلقي النتيجة حسب الحاجة.

أنصح ضد هذا رغم ذلك. 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