سؤال

لقد رأيت الكثير من الروابط التي تقدم قوالب variadic. لكنني لم أر أبدًا أي مثال قابل للتجميع يوضح هذا النهج.

هل يمكن لأي شخص أن يوفر لي بعض الروابط التي يمكن العثور فيها على هذه الأمثلة القابلة للتجميع؟

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

المحلول

قوالب variadic هي جزء من معيار C ++ 0x الذي لم يتم إصداره رسميًا بعد. يتم دعمها من قبل GCC منذ الإصدار 4.3 ، ولكن تحتاج إلى تمكين دعم C ++ 0x عن طريق إضافة مفتاح التحويل البرمجي -std = C ++ 0x.

نصائح أخرى

أحد أبسط الأمثلة الممكنة هو التنفيذ التالي لـ max التي لا تمثل حتى على الأنواع.

int maximum(int n)
{
    return n;
}

template<typename... Args>
int maximum(int n, Args... args)
{
    return max(n, maximum(args...));
}

أكثر تعقيدًا قليلاً هو الكنسي printf تطبيق:

void printf(const char *s)
{
  while (*s)
  {
    if (*s == '%' && *(++s) != '%')
      throw "invalid format string: missing arguments";
    std::cout << *s++;
  }
}

template<typename T, typename... Args>
void printf(const char* s, T value, Args... args)
{
  while (*s)
  {
    if (*s == '%' && *(++s) != '%')
    {
      std::cout << value;
      printf(s, args...); // call even when *s == 0 to detect extra arguments
      return;
    }
    std::cout << *s++;
  }
  throw "extra arguments provided to printf";
}

قوالب variadic هي ميزة C ++ 0x التي تستهدف في المقام الأول مؤلفي المكتبات العامة. لا أتوقع رؤيتها في "رمز المستخدم". على سبيل المثال ، في المكتبة القياسية C ++ 0x ، يتم استخدامها في الكثير من الأماكن: STD :: Function ، std :: async ، std :: reference_wrapper ، std :: tuple ، std :: packaged_task ، ...

لإعطائك مثالًا ، سأوضح لك كيف يمكن تنفيذ reference_wrapper فيما يتعلق بقوالب variadic:

template<class T>
class reference_wrapper
{
    T *ptr;
public:
    explicit reference_wrapper(T& thing) : ptr(&thing) {}
    explicit reference_wrapper(T&&     ) = delete;

    operator T&() const {return *ptr;}

    template<class... Args>
    decltype( declval<T&>()(declval<Args>()...) )
    operator()(Args&&... args) const
    {
        return (*ptr)(forward<Args>(args)...);
    }
};

هذا لا يتوافق تمامًا مع المسودة القياسية ولكن من المفترض أن يكون قابلاً للتجميع مع القليل من التعديل. يوضح العديد من ميزات C ++ 0x:

  • الوظائف المحذوفة (تعطيل مُنشئ RValues)
  • مراجع RVALUE (اكتشاف حجج RVALUE إلى المُنشئ ، إعادة التوجيه المثالي)
  • اكتب خصم عبر decltype
  • قالب وظيفة المكتبة القياسية declval لإنشاء كائنات لغرض بناء تعبير ل decltype (لا تقدم GCC هذا قالب الوظيفة هذا بعد. عليك أن تكتبه بنفسك)
  • قوالب متنوعة (قبول عدد تعسفي من المعلمات)

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

في صحتك! س

مثال بسيط للغاية على قالب المتغير:

لنفترض أننا نريد أن يكون لدينا وظيفة تأخذ عدد متغير من الوسائط وطباعةها جميعًا. لـ Ex:

print("Hello", 1, 3.14, 5L);

لكي تنجح هذه الوظيفة ، نطلب في الأساس وظيفتين:

أولاً ، وظيفة تأخذ عدد متغير من الوسائط:

template<typename T, typename... Args>
void print(T t, Args ...args){
     std::cout << t << ", ";
     print(args...);
}

بعض التفسير:

1.) حزم المعلمة التي يشار إليها بواسطة Ellipsis (...) ، والتي تظهر في قائمة المعلمات.

typename...Args 
        |  | << Optional whitespace. Can have multiple whitespaces in between them
    Args...args

وهذا يعني ، كل هذا هو نفسه.

typename ...args
typename...args
typename   ...   args

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

2.) توسع الحزمة: نمط يتبعه القطع الإهليل.

print(args...); //expand when you wish to use them

3.) تقبل حزمة المعلمة صفر أو أكثر قالب args. لذا، print(T t, Args... args) يقبل واحد أو أكثر args.


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

print("Hello", 1, 3.14, 5L);

ترجم إلى:

print(string, int, float, long);

الذي يدعو

print(int, float, long);

الذي يدعو

print(float, long);  // say Level 2

الذي يدعو

print(long);         // say Level 1

الذي يدعو

print();             // say Level 0

إذا كنت قد اتبعت النقطة رقم 3 بعناية ، فيجب أن تدرك ذلك print(T t, Args... args) لا يمكن التعامل مع المكالمة على المستوى 0.
لذلك نحن بحاجة إلى وظيفة أخرى هنا مع نفس الاسم للحاق في أي مستوى> = 0.


الثاني ، وظيفة ل إختطاف المكالمة في الجزء العلوي من مكدس المكالمات:

تمسك في المستوى 0:

void print(){}

أو ، تمسك في المستوى 1:

template<typename T>
void print(T t){ std::cout << t;}

أو ، اصطياد في المستوى 2:

template<typename T, typename U>
void print(T t, U u){ std::cout << t << ", " << u;}

قريباً...

أي من هؤلاء سيعمل. آمل أن يساعدك هذا في المرة القادمة في كتابة هذه الوظيفة أو الفصل.

هذا مثال على القوالب المتنوعة التي طرحتها على مدونتي:http://thenewcpp.wordpress.com/2011/11/23/variadic-templates-part-1-2/

يجمع. يوضح العثور على أكبر نوع من مجموعة من الأنواع.

#include <type_traits>

template <typename... Args>
struct find_biggest;

//the biggest of one thing is that one thing
template <typename First>
struct find_biggest<First>
{
  typedef First type;
};

//the biggest of everything in Args and First
template <typename First, typename... Args>
struct find_biggest<First, Args...>
{
  typedef typename find_biggest<Args...>::type next;
  typedef typename std::conditional
  <
    sizeof(First) >= sizeof(next),
    First,
    next
  >::type type;
};

قبل C ++ 11 ، يمكنك إنشاء قالب فقط مع العدد الثابت للمعلمات.

قالب FIRTS للوظيفة مع معلمة واحدة.

القالب الثاني للدالة مع معلمتين. ... بمعنى آخر

نظرًا لأن C ++ 11 يمكنك كتابة قالب واحد فقط ، فسيقوم برنامج التحويل البرمجي بإنشاء الوظيفة المطلوبة نفسها.

مثال جيدhttp://eli.thegreenplace.net/2014/variadic-templates-in-c/

بناء جملة آخر: التوسع ، على سبيل المثال

template<typename VAL, typename... KEYS>
class MyMaps
{
  typedef std::tuple< std::map<KEYS,VAL>... > Maps;
}

بالتالي:

MyMaps<int,int,string>:Maps

هو الآن في الواقع:

std::tuple<std::map<int,int>,std::map<string,int> >
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top