سؤال

لقد واجهت مفاجأة حقيقية في لغة C++، ولم يحدث لي ذلك من قبل.

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

يتم الإعلان عن وظيفة القالب الخاصة بي على النحو التالي:

template <typename T>
vector2<T> transform(vector2<T> const &vec, matrix4<T> const &m, T z = T(0), T w = T(1));

يتم تعريفه لاحقًا، في نفس الرأس، على النحو التالي:

template <typename T>
inline vector2<T> transform(vector2<T> const &vec, matrix4<T> const &m, T z, T w)
{
 vector4<T> res = m * vector4<T>(vec.x, vec.y, z, w);
 return vector2<T>(res.x, res.y);
}

الآن عندما أسمي هذا مع الإعدادات الافتراضية (transform(vector2<double>(0, 1), view_transform)) لا أحصل على القيم التي أتوقعها.الدخول في transform مع مصحح أخطاء VC++، أرى z و w وجود قيم "مضحكة" (وهذا يعني في تجربتي أنه لم تتم تهيئة شيء ما بشكل صحيح).

مثال على القيم المضحكة سيكون:0.0078125000000000000 و 2.104431116947e-317#DEN

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

هل هناك طريقة للحصول على القيم الافتراضية التي أتوقعها وأريدها، ولماذا تفعل ذلك بي؟

تحرير 1:

إذا قمت بإجراء مكالمة التغيير بحيث تستخدم العوامات بدلاً من ذلك (transform(vector2<float>(0, 1), view_transform)) المشكلة تختفي.يبدو أن هذا يحدث فقط إذا T = double.

تحرير 2:

يحدث ذلك فقط إذا كان لدي تخصصين double و float.إذا استخدمت تخصصًا عائمًا في مكان واحد، فسيحصل التخصص المزدوج على قيم افتراضية غريبة.إذا قمت بتغيير جميع الأماكن التي يتم استدعاء الوظيفة بحيث تستخدم مضاعفة فإن المشاكل "تختفي".ما زلت لا أفهم السبب، يبدو الأمر كما لو أنه يستخدم إزاحات خاطئة أو شيء من هذا القبيل عند الإعداد z و w.

تحرير 3:

حكايات من C++ Crypt:

#include <sgt/matrix4.hpp>

int main(int argc, char *argv[])
{
    sgt::matrix4<double> m0(
        2, 0, 0, 1,
        0, 2, 0, 1,
        0, 0, 1, 0,
        0, 0, 0, 1);

    m0 *= m0;

    sgt::vector2<double> blah0 = sgt::transform(sgt::vector2<double>(1, 0), m0);

    sgt::matrix4<float> m1(
        2, 0, 0, 1,
        0, 2, 0, 1,
        0, 0, 1, 0,
        0, 0, 0, 1);

    m1 *= m1;

    sgt::vector2<float> blah1 = sgt::transform(sgt::vector2<float>(1, 0), m1);

    printf("%f", blah0.x);
    printf("%f", blah1.x);
}

في Matrix4.hpp:

// ...

template <typename T>
vector2<T> transform(vector2<T> const &vec, matrix4<T> const &m, T z = T(0), T w = T(1));

template <typename T>
inline vector2<T> transform(vector2<T> const &vec, matrix4<T> const &m, T z, T w)
{
    vector4<T> res = m * vector4<T>(vec.x, vec.y, z, w);
    return vector2<T>(res.x, res.y);
}

// ...

إذا قمت بتشغيل ذلك، فإن التخصص المزدوج يحتوي على وسيطاته الافتراضية صحيحة، لكن الإصدار العائم يحصل على الوسيطات الافتراضية على أنها صفر (0.000000) وهو على الرغم من أنه أفضل، إلا أنه لا يزال كذلك z = 0 و w = 1.

تحرير 4:

صنع قضية الاتصال.

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

المحلول

فشل ما يلي في Dev Studio:

#include "stdafx.h"
#include <vector>
#include <iostream>

template <typename T>
std::vector<std::vector<T> > transform(std::vector<std::vector<T> > const &vec,
                                       std::vector<std::vector<std::vector<std::vector<T> > > > const &m,
                                       T z = T(0), T w = T(1));


template <typename T>
std::vector<std::vector<T> > transform(std::vector<std::vector<T> > const &vec,
                                       std::vector<std::vector<std::vector<std::vector<T> > > > const &m,
                                       T z, T w)
{
    std::cout << "Z" << z << "\n";
    std::cout << "W" << w << "\n";

    return vec;
}

int _tmain(int argc, _TCHAR* argv[])
{
    std::vector<std::vector<int> >  xi;
    std::vector<std::vector<std::vector<std::vector<int> > > > mi;
    transform(xi,mi);

    std::vector<std::vector<float> >    xf;
    std::vector<std::vector<std::vector<std::vector<float> > > > mf;
    transform(xf,mf);

    std::vector<std::vector<double> >   xd;
    std::vector<std::vector<std::vector<std::vector<double> > > > md;
    transform(xd,md);
}

انتاج:

Z0
W1
Z0
W1.4013e-045
Z2.122e-314
W3.60689e-305

لذلك أعتقد أنه لا يعمل كما هو متوقع!!!

إذا قمت بإزالة الإعلان المسبق ووضع الوسائط الافتراضية في وظيفة القالب، فسيعمل كما هو متوقع.

#include "stdafx.h"
#include <vector>
#include <iostream>

template <typename T>
std::vector<std::vector<T> > transform(std::vector<std::vector<T> > const &vec,
                                       std::vector<std::vector<std::vector<std::vector<T> > > > const &m
                                       T z = T(0), T w = T(1))
{
    std::cout << "Z" << z << "\n";
    std::cout << "W" << w << "\n";

    return vec;
}

int _tmain(int argc, _TCHAR* argv[])
{
    std::vector<std::vector<int> >  xi;
    std::vector<std::vector<std::vector<std::vector<int> > > > mi;
    transform(xi,mi);

    std::vector<std::vector<float> >    xf;
    std::vector<std::vector<std::vector<std::vector<float> > > > mf;
    transform(xf,mf);

    std::vector<std::vector<double> >   xd;
    std::vector<std::vector<std::vector<std::vector<double> > > > md;
    transform(xd,md);
}

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

نعم.ليس من قراءتي للمعيار أن هذا يجب أن يعمل كما هو متوقع:

باستخدام n2521
القسم 14.7.1 الإنشاء الضمني
الفقرة 9

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

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

الفقرة 11:

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

يشير إلى أنه حتى لو كانت الوسائط الافتراضية عبارة عن معلمات قالب، فسيتم إنشاء مثيل لها بشكل صحيح.

حسنًا، آمل أن أكون قد فسرت ذلك بشكل صحيح.:-)

نصائح أخرى

هل تم تحسين الكود؟ربما لهذا السبب يعرض لك مصحح الأخطاء القيم الخاطئة.

لقد جربت هذا الكود الأبسط (في g++ 4.3.3) وهو يعمل كما هو متوقع.

template <typename T>
T increment(T a, T b = T(1))
{
    return a + b;
}

int main()
{
    double a = 5.0;
    std::cout << increment(a) << ", ";
    std::cout << increment(a, 3.0) << "\n";
}

لا أعرف ما إذا كان هذا سينجح، ولكن حاول استخدام static_cast بدلاً من قالب نمط C لقيمك الافتراضية.

*يحرر: على ما يبدو، المشكلة هي المترجم.

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