سؤال

لماذا لا تدعم اللغات المكتوبة بشكل ثابت التحميل الزائد للوظيفة/الطريقة حسب نوع الإرجاع؟لا أستطيع التفكير في أي شيء يفعله.يبدو أنه ليس أقل فائدة أو معقولية من دعم التحميل الزائد حسب نوع المعلمة.لماذا هي أقل شعبية بكثير؟

هل كانت مفيدة؟

المحلول

على عكس ما يقوله الآخرون، التحميل الزائد حسب نوع الإرجاع يكون ممكن و يكون التي قامت بها بعض اللغات الحديثة.الاعتراض المعتاد هو أنه في الكود مثل

int func();
string func();
int main() { func(); }

لا يمكنك معرفة أي منها func() يتم استدعاؤه.يمكن حل هذا بعدة طرق:

  1. لديك طريقة يمكن التنبؤ بها لتحديد الوظيفة التي يتم استدعاؤها في مثل هذه الحالة.
  2. كلما حدث مثل هذا الموقف، فهو خطأ في وقت الترجمة.ومع ذلك، يجب أن يكون لديك بناء جملة يسمح للمبرمج بإزالة الغموض، على سبيل المثال: int main() { (string)func(); }.
  3. ليس لها آثار جانبية.إذا لم يكن لديك آثار جانبية ولم تستخدم أبدًا القيمة المرجعة للدالة، فيمكن للمترجم تجنب استدعاء الوظيفة في المقام الأول.

اثنين من اللغات التي أستخدمها بانتظام (أب)استخدام التحميل الزائد حسب نوع الإرجاع: بيرل و هاسكل.اسمحوا لي أن أصف ما يفعلونه.

في بيرل, ، هناك فرق أساسي بين العددية و قائمة السياق (وغيرها، لكننا سنتظاهر بوجود اثنين).يمكن لكل وظيفة مدمجة في Perl القيام بأشياء مختلفة اعتمادًا على سياق الذي يطلق عليه.على سبيل المثال، join يفرض عامل التشغيل سياق القائمة (على الشيء الذي يتم ضمه) بينما scalar يفرض المشغل سياقًا عدديًا، لذا قارن:

print join " ", localtime(); # printed "58 11 2 14 0 109 3 13 0" for me right now
print scalar localtime(); # printed "Wed Jan 14 02:12:44 2009" for me right now.

كل عامل في لغة Perl يفعل شيئًا ما في سياق عددي وشيء ما في سياق القائمة، وقد يكونان مختلفين، كما هو موضح.(هذا ليس فقط للمشغلين العشوائيين مثل localtime.إذا كنت تستخدم مصفوفة @a في سياق القائمة، تقوم بإرجاع المصفوفة، بينما في السياق العددي، تقوم بإرجاع عدد العناصر.هكذا على سبيل المثال print @a يطبع العناصر، في حين print 0+@a يطبع الحجم.) علاوة على ذلك، يمكن لكل مشغل قوة سياق، على سبيل المثالإضافة + قوى السياق العددي.كل دخول في man perlfunc يوثق هذا.على سبيل المثال، هنا جزء من الإدخال لـ glob EXPR:

في سياق القائمة ، إرجاع قائمة (ربما فارغة) لتوسعات اسم الملف على قيمة EXPR مثل قذيفة UNIX القياسية /bin/csh ستفعل.في سياق العدسي ، يتكرر GLOB من خلال توسعات اسم الملف هذه ، وإعادة UNDEF عند استنفاد القائمة.

الآن، ما هي العلاقة بين القائمة والسياق العددي؟حسنًا، man perlfunc يقول

تذكر القاعدة المهمة التالية:لا توجد قاعدة تتعلق ب سلوك تعبير في القائمة سياق سلوكها في العددية السياق ، أو العكس.قد تفعل شيئان مختلفان تماما.كل يقرر المشغل والوظيفة أيهما نوع من القيمة سيكون أكثر مناسب للعودة في عددي سياق.يقوم بعض المشغلين بإرجاع طول القائمة التي من شأنها أن يكون تم إرجاعها في سياق القائمة.بعض يقوم عامل التشغيل بإرجاع القيمة الأولى في القائمة.يقوم بعض المشغلين بإرجاع القيمة الأخيرة في القائمة.بعض يعيد المشغلون عدد الناجحين العمليات.بشكل عام ، يفعلون ماذا تريد ، إلا إذا كنت تريد الاتساق.

لذلك ليس من السهل أن يكون لديك وظيفة واحدة، ثم تقوم بإجراء تحويل بسيط في النهاية.في الواقع، لقد اخترت localtime مثال لهذا السبب.

ليس فقط العناصر المدمجة هي التي لديها هذا السلوك.يمكن لأي مستخدم تحديد مثل هذه الوظيفة باستخدام wantarray, ، والذي يسمح لك بالتمييز بين سياق القائمة والعددية والباطلة.لذلك، على سبيل المثال، يمكنك أن تقرر عدم القيام بأي شيء إذا تم الاتصال بك في سياق فارغ.

الآن، قد تشكو من أن هذا ليس كذلك حقيقي التحميل الزائد من خلال قيمة الإرجاع لأن لديك وظيفة واحدة فقط، والتي يتم إخبارها بالسياق الذي يتم استدعاؤه فيه ثم تعمل بناءً على تلك المعلومات.ومع ذلك، من الواضح أن هذا مكافئ (ومماثل لكيفية عدم سماح لغة Perl بالتحميل الزائد المعتاد حرفيًا، ولكن يمكن للوظيفة فقط فحص وسيطاتها).علاوة على ذلك، فهو يحل بشكل جيد الموقف الغامض المذكور في بداية هذا الرد.لا يشكو بيرل من أنه لا يعرف الطريقة التي يجب الاتصال بها؛يطلق عليه فقط.كل ما عليها فعله هو معرفة السياق الذي تم استدعاء الوظيفة فيه، وهو أمر ممكن دائمًا:

sub func {
    if( not defined wantarray ) {
        print "void\n";
    } elsif( wantarray ) {
        print "list\n";
    } else {
        print "scalar\n";
    }
}

func(); # prints "void"
() = func(); # prints "list"
0+func(); # prints "scalar"

(ملحوظة:قد أقول أحيانًا عامل Perl عندما أقصد الوظيفة.وهذا ليس حاسما في هذه المناقشة.)

هاسكل يأخذ النهج الآخر، وهو عدم وجود آثار جانبية.كما أن لديها نظام كتابة قوي، ويمكنك كتابة التعليمات البرمجية مثل ما يلي:

main = do n <- readLn
          print (sqrt n) -- note that this is aligned below the n, if you care to run this

يقرأ هذا الرمز رقم الفاصلة العائمة من الإدخال القياسي، ويطبع جذره التربيعي.ولكن ما الذي يثير الدهشة في هذا؟حسنا، نوع readLn يكون readLn :: Read a => IO a.ما يعنيه هذا هو أنه لأي نوع يمكن أن يكون Read (رسميًا، كل نوع يمثل مثيلًا لـ Read فئة النوع)، readLn يمكن قراءتها.كيف عرف هاسكل أنني أريد قراءة رقم الفاصلة العائمة؟حسنا، نوع sqrt يكون sqrt :: Floating a => a -> a, ، وهو ما يعني ذلك في الأساس sqrt يمكنه فقط قبول أرقام الفاصلة العائمة كمدخلات، ولذلك استنتج هاسكل ما أردت.

ماذا يحدث عندما لا يتمكن هاسكل من استنتاج ما أريد؟حسنًا، هناك بعض الاحتمالات.إذا لم أستخدم القيمة المرجعة على الإطلاق، فلن يقوم هاسكل ببساطة باستدعاء الوظيفة في المقام الأول.ومع ذلك، إذا كنت يفعل استخدم القيمة المرجعة، ثم سيشتكي هاسكل من عدم قدرته على استنتاج النوع:

main = do n <- readLn
          print n
-- this program results in a compile-time error "Unresolved top-level overloading"

يمكنني حل الغموض عن طريق تحديد النوع الذي أريده:

main = do n <- readLn
          print (n::Int)
-- this compiles (and does what I want)

على أية حال، ما تعنيه هذه المناقشة بأكملها هو أن التحميل الزائد من خلال القيمة المرجعة أمر ممكن ويتم تنفيذه، وهو ما يجيب على جزء من سؤالك.

الجزء الآخر من سؤالك هو لماذا لا تفعل ذلك المزيد من اللغات.سأدع الآخرين يجيبون على ذلك.ومع ذلك، بعض التعليقات:ربما يكون السبب الرئيسي هو أن احتمال حدوث ارتباك هنا أكبر حقًا منه في التحميل الزائد حسب نوع الوسيطة.يمكنك أيضًا الاطلاع على المبررات من اللغات الفردية:

آدا:"قد يبدو أن أبسط قاعدة لحل مشكلة التحميل الزائد هي استخدام كل شيء - جميع المعلومات من أكبر سياق ممكن - لحل المرجع الزائد التحميل.قد تكون هذه القاعدة بسيطة، ولكنها ليست مفيدة.فهو يتطلب من القارئ البشري أن يقوم بمسح أجزاء كبيرة من النص بشكل تعسفي، وإجراء استنتاجات معقدة بشكل تعسفي (مثل (ز) أعلاه).نحن نعتقد أن القاعدة الأفضل هي تلك التي توضح المهمة التي يجب على القارئ البشري أو المترجم القيام بها، وهذا يجعل هذه المهمة طبيعية للقارئ البشري قدر الإمكان.

C++ (القسم الفرعي 7.4.1 من "لغة برمجة C++" لبيارن ستروستروب):"لا يتم أخذ أنواع الإرجاع في الاعتبار عند تحليل التحميل الزائد.والسبب هو الحفاظ على دقة عامل التشغيل الفردي أو استدعاء الوظيفة بشكل مستقل عن السياق.يعتبر:

float sqrt(float);
double sqrt(double);

void f(double da, float fla)
{
    float fl = sqrt(da);     // call sqrt(double)
    double d = sqrt(da); // call sqrt(double)
    fl = sqrt(fla);            // call sqrt(float)
    d = sqrt(fla);             // call sqrt(float)
}

إذا تم أخذ نوع الإرجاع في الاعتبار، فلن يكون من الممكن النظر في استدعاء sqrt() بشكل منفصل وتحديد الوظيفة التي تم استدعاؤها." (لاحظ، للمقارنة، أنه في هاسكل لا توجد ضمني التحويلات.)

جافا (مواصفات لغة جافا 9.4.1):"يجب أن تكون إحدى الطرق الموروثة من نوع الإرجاع القابل للاستبدال لأي طريقة موروثة أخرى؛وإلا، فسيحدث خطأ في وقت الترجمة." (نعم، أعلم أن هذا لا يعطي سببًا منطقيًا.أنا متأكد من أن جوسلينج قدم الأساس المنطقي في "لغة برمجة جافا".ربما شخص ما لديه نسخة؟أراهن أنه "مبدأ الأقل مفاجأة" في جوهره.) ومع ذلك، حقيقة ممتعة حول جافا:JVM يسمح الحمولة الزائدة عن طريق قيمة الإرجاع!ويستخدم هذا، على سبيل المثال، في سكالا, ، ويمكن الوصول إليها مباشرة من خلال جافا وكذلك من خلال اللعب مع العناصر الداخلية.

ملاحظة.كملاحظة أخيرة، من الممكن بالفعل زيادة التحميل من خلال قيمة الإرجاع في C++ باستخدام خدعة.شاهد:

struct func {
    operator string() { return "1";}
    operator int() { return 2; }
};

int main( ) {
    int x    = func(); // calls int version
    string y = func(); // calls string version
    double d = func(); // calls int version
    cout << func() << endl; // calls int version
    func(); // calls neither
}

نصائح أخرى

إذا كانت مثقلة الوظائف عن طريق نوع الإرجاع وكان لديك هذه الزائدة اثنين

int func();
string func();

وليس هناك طريقة المترجم يمكن معرفة أي من تلك الوظيفتين دعوة رؤية مكالمة من هذا القبيل

void main() 
{
    func();
}

لهذا السبب، ومصممي اللغة في كثير من الأحيان عدم السماح الحمولة الزائدة عودة القيمة.

وبعض اللغات (مثل MSIL)، ومع ذلك، <م> لا تسمح الحمولة الزائدة من نوع الإرجاع. هم أيضا يواجهون صعوبة أعلاه بطبيعة الحال، ولكن لديهم الحلول، والتي سيكون لديك لمراجعة الوثائق الخاصة بهم.

في هذه اللغة، وكيف ستحل ما يلي:

f(g(x))

وإذا كان f الزائدة void f(int) وكان void f(string) وg الزائدة int g(int) وstring g(int)؟ قد تحتاج نوعا من disambiguator.

وأعتقد أن الحالات التي قد تحتاج هذا من شأنه أن يكون أفضل عمل عن طريق اختيار اسم جديد لهذه الوظيفة.

لسرقة من سؤال مشابه جدا آخر (خداع؟):


وأنواع وظيفة عائد لا تدخل في الاعتبار في قرار الزائد لأنه ببساطة Stroustrup (أفترض مع مدخلات من المهندسين المعماريين C ++ الأخرى) أراد قرار الزائد أن يكون "سياق مستقل. انظر 7.4.1 - "الحمولة الزائدة وعودة نوع" من "C ++ لغة برمجة، الطبعة الثالثة"

<اقتباس فقرة>   

والسبب هو الحفاظ على قرار لمشغل فردي أو استدعاء دالة سياق مستقل.

وأرادوا لها أن تستند فقط على كيفية تم استدعاء الزائد - وليس كيف تم استخدام نتيجة (إذا تم استخدامه على الإطلاق). في الواقع، وتسمى العديد من الوظائف دون استخدام نتيجة أو ستستخدم نتيجة كجزء من تعبير أكبر. جاء أحد العوامل التي أنا متأكد في اللعب عندما قررت هذا كان أنه إذا كان نوع الإرجاع جزء من القرار سيكون هناك العديد من المكالمات إلى وظائف مثقلة التي من شأنها أن تحتاج إلى حل مع قواعد معقدة أو أن يكون لديك رمي مترجم خطأ أن الدعوة كانت غامضة.

و، الرب يعلم، القرار الزائد C ++ معقد بما فيه الكفاية كما هو عليه ...

في haskell يكون ذلك ممكنًا على الرغم من عدم وجود تحميل زائد للوظيفة.يستخدم هاسكل فئات الكتابة.في البرنامج يمكنك أن ترى:

class Example a where
    example :: Integer -> a

instance Example Integer where  -- example is now implemented for Integer
    example :: Integer -> Integer
    example i = i * 10

التحميل الزائد للوظيفة في حد ذاته لا يحظى بشعبية كبيرة.معظم اللغات التي رأيتها معها هي C++، وربما Java و/أو C#.في جميع اللغات الديناميكية، يعد هذا اختصارًا لـ:

define example:i
  ↑i type route:
    Integer = [↑i & 0xff]
    String = [↑i upper]


def example(i):
    if isinstance(i, int):
        return i & 0xff
    elif isinstance(i, str):
        return i.upper()

لذلك ليس هناك فائدة كبيرة في ذلك.لا يهتم معظم الأشخاص بما إذا كانت اللغة يمكن أن تساعدك على إسقاط سطر واحد في أي مكان تستخدمه فيه.

تشبه مطابقة الأنماط إلى حد ما التحميل الزائد للوظيفة، وأعتقد أنها تعمل أحيانًا بشكل مشابه.ومع ذلك، فهو ليس شائعًا لأنه مفيد فقط لعدد قليل من البرامج ويصعب تنفيذه على معظم اللغات.

ترى أن هناك عددًا لا نهائيًا من الميزات الأخرى الأفضل والأسهل في التنفيذ والتي يمكن تنفيذها في اللغة، بما في ذلك:

  • الكتابة الديناميكية
  • الدعم الداخلي للقوائم والقواميس وسلاسل يونيكود
  • التحسينات (JIT، استنتاج النوع، التجميع)
  • أدوات النشر المتكاملة
  • دعم المكتبة
  • دعم المجتمع وأماكن التجمع
  • مكتبات قياسية غنية
  • بناء جملة جيد
  • قراءة حلقة الطباعة التقييمية
  • دعم البرمجة العاكسة

وإجابات جيدة! الجواب A.Rex على وجه الخصوص مفصلة جدا ومفيدة. كما يشير، C ++ <م> لا النظر مشغلي نوع التحويل يقدمها المستخدم عند تجميع lhs = func(); <م> (حيث ظائفها هو حقا اسم البنية) . بلدي الحل هو مختلفة قليلا - لم يكن أفضل، مختلفة تماما (على الرغم من أنها تقوم على نفس الفكرة الأساسية)

وبينما كان <م> أردت لكتابة ...

template <typename T> inline T func() { abort(); return T(); }

template <> inline int func()
{ <<special code for int>> }

template <> inline double func()
{ <<special code for double>> }

.. etc, then ..

int x = func(); // ambiguous!
int x = func<int>(); // *also* ambiguous!?  you're just being difficult, g++!

وانتهى بي الأمر مع الحل الذي يستخدم البنية معلمات (مع T = نوع العودة):

template <typename T>
struct func
{
    operator T()
    { abort(); return T(); } 
};

// explicit specializations for supported types
// (any code that includes this header can add more!)

template <> inline
func<int>::operator int()
{ <<special code for int>> }

template <> inline
func<double>::operator double()
{ <<special code for double>> }

.. etc, then ..

int x = func<int>(); // this is OK!
double d = func<double>(); // also OK :)

وهناك فائدة من هذا الحل هو أن أي رمز والذي يتضمن هذه التعريفات قالب يمكن إضافة المزيد من التخصصات لمزيد من أنواع. أيضا يمكنك أن تفعل التخصصات جزئية من البنية حسب الحاجة. على سبيل المثال، إذا أردت معالجة خاصة لأنواع المؤشر:

template <typename T>
struct func<T*>
{
    operator T*()
    { <<special handling for T*>> } 
};

وأما السلبية، لا يمكنك كتابة int x = func(); مع بلدي الحل. لديك لكتابة int x = func<int>();. لديك لتقوله صراحة ما هو نوع الإرجاع، بدلا من أن يسأل المترجم لسوس بها من خلال النظر في شركات تحويل نوع. أود أن أقول أن "بلادي" حل وفي A.Rex كل من ينتمي في <لأ href = "http://en.wikipedia.org/wiki/Pareto_efficiency#Pareto_frontier" يختلط = "نوفولو" عنوان = "مجموعة من عدم الحلول التي يسيطر عليها ">-باريتو الأمثل أمام من الطرق لمعالجة هذا C ++ معضلة:)

وإذا كنت تريد الزائد طرق مع أنواع الإرجاع مختلفة، مجرد إضافة على المعلمة وهمية مع القيمة الافتراضية لتسمح بتنفيذ الزائد، ولكن لا ننسى نوع المعلمة يجب أن تكون مختلفة وبالتالي فإن المنطق الزائد يعمل القادم هو على سبيل المثال في دلفي:

type    
    myclass = class
    public
      function Funct1(dummy: string = EmptyStr): String; overload;
      function Funct1(dummy: Integer = -1): Integer; overload;
    end;

واستخدامه مثل هذا

procedure tester;
var yourobject : myclass;
  iValue: integer;
  sValue: string;
begin
  yourobject:= myclass.create;
  iValue:= yourobject.Funct1(); //this will call the func with integer result
  sValue:= yourobject.Funct1(); //this will call the func with string result
end;

وكما هو مبين بالفعل - مكالمات غامضة وظيفة التي تختلف فقط من نوع الإرجاع يقدم الغموض. الغموض يدفع كود معيب. يجب تجنب كود معيب.

وتعقيد مدفوعا محاولة لالغموض ويظهر أن هذا ليس الإختراق جيد. وبصرف النظر عن ممارسة فكرية - لماذا لا تستخدم الإجراءات مع المعايير المرجعية

procedure(reference string){};
procedure(reference int){};
string blah;
procedure(blah)

وهذه ميزة التحميل الزائد ليس من الصعب إدارة، إذا نظرتم اليها بطريقة مختلفة قليلا. النظر في ما يلي،

public Integer | String f(int choice){
if(choice==1){
return new string();
}else{
return new Integer();
}}

وإذا كان لغة لم يعودوا إثقال انها ستسمح الحمولة الزائدة المعلمة، ولكن ليس الازدواجية. هذا من شأنه أن يحل مشكلة:

main (){
f(x)
}

ولأن هناك واحد فقط و (كثافة العمليات الاختيار) للاختيار من بينها.

في .NET، أحيانا نستخدم معلمة واحدة للدلالة على النتيجة المرجوة من نتيجة عامة، ومن ثم قدم تحويل للحصول على ما نتوقعه.

C #

public enum FooReturnType{
        IntType,
        StringType,
        WeaType
    }

    class Wea { 
        public override string ToString()
        {
            return "Wea class";
        }
    }

    public static object Foo(FooReturnType type){
        object result = null;
        if (type == FooReturnType.IntType) 
        {
            /*Int related actions*/
            result = 1;
        }
        else if (type == FooReturnType.StringType)
        {
            /*String related actions*/
            result = "Some important text";
        }
        else if (type == FooReturnType.WeaType)
        {
            /*Wea related actions*/
            result = new Wea();
        }
        return result;
    }

    static void Main(string[] args)
    {
        Console.WriteLine("Expecting Int from Foo: " + Foo(FooReturnType.IntType));
        Console.WriteLine("Expecting String from Foo: " + Foo(FooReturnType.StringType));
        Console.WriteLine("Expecting Wea from Foo: " + Foo(FooReturnType.WeaType));
        Console.Read();
    }

وربما هذا المثال يمكن أن يساعد أيضا:

C ++

    #include <iostream>

enum class FooReturnType{ //Only C++11
    IntType,
    StringType,
    WeaType
}_FooReturnType;

class Wea{
public:
    const char* ToString(){
        return "Wea class";
    }
};

void* Foo(FooReturnType type){
    void* result = 0;
    if (type == FooReturnType::IntType) //Only C++11
    {
        /*Int related actions*/
        result = (void*)1;
    }
    else if (type == FooReturnType::StringType) //Only C++11
    {
        /*String related actions*/
        result = (void*)"Some important text";
    }
    else if (type == FooReturnType::WeaType) //Only C++11
    {
        /*Wea related actions*/
        result = (void*)new Wea();
    }
    return result;
}

int main(int argc, char* argv[])
{
    int intReturn = (int)Foo(FooReturnType::IntType);
    const char* stringReturn = (const char*)Foo(FooReturnType::StringType);
    Wea *someWea = static_cast<Wea*>(Foo(FooReturnType::WeaType));
    std::cout << "Expecting Int from Foo: " << intReturn << std::endl;
    std::cout << "Expecting String from Foo: " << stringReturn << std::endl;
    std::cout << "Expecting Wea from Foo: " << someWea->ToString() << std::endl;
    delete someWea; // Don't leak oil!
    return 0;
}

للعلم، اوكتاف يسمح بنتائج مختلفة وفقًا لعنصر الإرجاع الذي يكون عدديًا مقابل المصفوفة.

x = min ([1, 3, 0, 2, 0])
   ⇒  x = 0

[x, ix] = min ([1, 3, 0, 2, 0])
   ⇒  x = 0
      ix = 3 (item index)

راجع أيضا تحليل القيمة المفردة.

هذا يختلف قليلاً عن C++؛لا أعرف ما إذا كان سيتم اعتبار ذلك بمثابة تحميل زائد عن طريق نوع الإرجاع مباشرةً.إنه أكثر من مجرد قالب متخصص يعمل بطريقة.

util.h

#ifndef UTIL_H
#define UTIL_H

#include <string>
#include <sstream>
#include <algorithm>

class util {
public: 
    static int      convertToInt( const std::string& str );
    static unsigned convertToUnsigned( const std::string& str );
    static float    convertToFloat( const std::string& str );
    static double   convertToDouble( const std::string& str );

private:
    util();
    util( const util& c );
    util& operator=( const util& c );

    template<typename T>
    static bool stringToValue( const std::string& str, T* pVal, unsigned numValues );

    template<typename T>
    static T getValue( const std::string& str, std::size_t& remainder );
};

#include "util.inl"

#endif UTIL_H

util.inl

template<typename T>
static bool util::stringToValue( const std::string& str, T* pValue, unsigned numValues ) {
    int numCommas = std::count(str.begin(), str.end(), ',');
    if (numCommas != numValues - 1) {
        return false;
    }

    std::size_t remainder;
    pValue[0] = getValue<T>(str, remainder);

    if (numValues == 1) {
        if (str.size() != remainder) {
            return false;
        }
    }
    else {
        std::size_t offset = remainder;
        if (str.at(offset) != ',') {
            return false;
        }

        unsigned lastIdx = numValues - 1;
        for (unsigned u = 1; u < numValues; ++u) {
            pValue[u] = getValue<T>(str.substr(++offset), remainder);
            offset += remainder;
            if ((u < lastIdx && str.at(offset) != ',') ||
                (u == lastIdx && offset != str.size()))
            {
                return false;
            }
        }
    }
    return true;    
}

util.cpp

#include "util.h"

template<>
int util::getValue( const std::string& str, std::size_t& remainder ) {
    return std::stoi( str, &remainder );
} 

template<>
unsigned util::getValue( const std::string& str, std::size_t& remainder ) {
    return std::stoul( str, &remainder );
}

template<>
float util::getValue( const std::string& str, std::size_t& remainder ) {
    return std::stof( str, &remainder );
}     

template<>   
double util::getValue( const std::string& str, std::size_t& remainder ) {
    return std::stod( str, &remainder );
}

int util::convertToInt( const std::string& str ) {
    int i = 0;
    if ( !stringToValue( str, &i, 1 ) ) {
        std::ostringstream strStream;
        strStream << __FUNCTION__ << " Bad conversion of [" << str << "] to int";
        throw strStream.str();
    }
    return i;
}

unsigned util::convertToUnsigned( const std::string& str ) {
    unsigned u = 0;
    if ( !stringToValue( str, &u, 1 ) ) {
        std::ostringstream strStream;
        strStream << __FUNCTION__ << " Bad conversion of [" << str << "] to unsigned";
        throw strStream.str();
    }
    return u;
}     

float util::convertToFloat(const std::string& str) {
    float f = 0;
    if (!stringToValue(str, &f, 1)) {
        std::ostringstream strStream;
        strStream << __FUNCTION__ << " Bad conversion of [" << str << "] to float";
        throw strStream.str();
    }
    return f;
}

double util::convertToDouble(const std::string& str) {
    float d = 0;
    if (!stringToValue(str, &d, 1)) {
        std::ostringstream strStream;
        strStream << __FUNCTION__ << " Bad conversion of [" << str << "] to double";
        throw strStream.str();
    }
    return d;
}

هذا المثال لا يستخدم بالضبط تحليل التحميل الزائد للوظيفة عن طريق نوع الإرجاع، ولكن فئة c++ غير الكائنية تستخدم تخصص القالب لمحاكاة تحليل التحميل الزائد للوظيفة عن طريق نوع الإرجاع باستخدام طريقة ثابتة خاصة.

كل من convertToType تقوم الوظائف باستدعاء قالب الوظيفة stringToValue() وإذا نظرت إلى تفاصيل التنفيذ أو الخوارزمية الخاصة بقالب الوظيفة هذا، فهي تستدعي getValue<T>( param, param ) وهو يعود نوعاً T وتخزينها في T* الذي يتم تمريره إلى stringToValue() قالب الوظيفة كأحد معلماته.

بخلاف شيء من هذا القبيل؛ليس لدى C++ حقًا آلية للحصول على حل التحميل الزائد للوظيفة حسب نوع الإرجاع.قد تكون هناك بنيات أو آليات أخرى لست على علم بها والتي يمكنها محاكاة الدقة حسب نوع الإرجاع.

وأنا أعتقد أن هذا هو GAP في C ++ الحديثة تعريف ... لماذا؟

int func();
double func();

// example 1. → defined
int i = func();

// example 2. → defined
double d = func();

// example 3. → NOT defined. error
void main() 
{
    func();
}

وماذا يمكن للمترجم C ++ لا يمكن الإلقاء خطأ في المثال "3" و استعرض التعليمات البرمجية في المثال "1 + 2" ؟؟

واللغات معظم ثابتة كما تدعم الآن الوراثة، والتي من شأنها حل مشكلتك. كما ذكر من قبل، دون الحاجة بيانات الاختلاف المعلمة، وليس هناك طريقة لمعرفة أي واحد للاتصال. حتى إذا كنت تريد أن تفعل ذلك، ومجرد استخدام الأدوية ويطلق عليه اليوم.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top