خصم القالب للوظيفة بناءً على نوع الإرجاع؟
-
25-09-2019 - |
سؤال
أود أن أكون قادرًا على استخدام خصم القالب لتحقيق ما يلي:
GCPtr<A> ptr1 = GC::Allocate();
GCPtr<B> ptr2 = GC::Allocate();
بدلاً من (ما لدي حاليًا):
GCPtr<A> ptr1 = GC::Allocate<A>();
GCPtr<B> ptr2 = GC::Allocate<B>();
تبدو وظيفة تخصيص بلدي الحالية مثل هذا:
class GC
{
public:
template <typename T>
static GCPtr<T> Allocate();
};
هل سيكون هذا ممكنًا لإيقاف الإضافة <A>
و <B>
?
المحلول
لا يمكن القيام بذلك. لا يشارك نوع الإرجاع في خصم النوع ، بل إنه نتيجة لمطابقة توقيع القالب المناسب بالفعل. يمكنك ، مع ذلك ، إخفاءها من معظم الاستخدامات على النحو التالي:
// helper
template <typename T>
void Allocate( GCPtr<T>& p ) {
p = GC::Allocate<T>();
}
int main()
{
GCPtr<A> p = 0;
Allocate(p);
}
سواء كان هذا الجملة هو في الواقع أفضل أو أسوأ من الأولي GCPtr<A> p = GC::Allocate<A>()
هو سؤال آخر.
سيسمح لك PS C ++ 11 بتخطي أحد إعلانات النوع:
auto p = GC::Allocate<A>(); // p is of type GCPtr<A>
نصائح أخرى
الشيء الوحيد الذي يمكنني التفكير فيه: اجعل تخصيصًا غير مملوك يعيد كائن وكيل غير مملوك له مشغل تحويل تمثل يقوم بالعمل الحقيقي:
template <class T>
struct GCPtr
{
};
class Allocator
{
public:
template <class T>
operator GCPtr<T>() { return GCPtr<T>(); }
};
class GC
{
public:
static Allocator Allocate() { return Allocator(); }//could give a call-back pointer?
};
int main()
{
GCPtr<int> p = GC::Allocate();
}
هل يمكن أن تسير في الطريق المعاكس.
إذا كنت تستخدم برنامج التحويل البرمجي المحدث (MSVC 2010 والذي يجب أن يكون في غضون يومين ، أو الإصدار الحالي من GCC) ولا تمانع في الاعتماد على ميزات C ++ 0x:
auto ptr1 = GC::Allocate<A>();
auto ptr2 = GC::Allocate<B>();
سوف يوفر لك الإضافية <A>
و <B>
, ، ليس فقط على الجانب الأيمن. قون
(هذه الإجابة هي نفسها مثل unclebens ، ولكنها أكثر عمومية لأنها مثالية إلى الأمام.
هذا مفيد جدًا في لغات مثل Haskell حيث ، على سبيل المثال ، read
سوف تأخذ سلسلة كمدخلات وسوف تحليلها وفقا لنوع الإرجاع المطلوب.
(هنا عينة رمز على ideone.)
أولاً ، ابدأ بالوظيفة foo
من نوع العودة نود أن نستنتج:
template<typename Ret>
Ret foo(const char *,int);
template<>
std::string foo<std::string>(const char *s,int) { return s; }
template<>
int foo<int >(const char *,int i) { return i; }
عندما يُطلب من سلسلة ، ستعيد السلسلة الموجودة في الوسيطة الأولى. عندما يُطلب من int ، ستعيد الوسيطة الثانية.
يمكننا تحديد وظيفة auto_foo
يمكن استخدامها على النحو التالي:
int main() {
std::string s = auto_foo("hi",5); std::cout << s << std::endl;
int i = auto_foo("hi",5); std::cout << i << std::endl;
}
لجعل هذا العمل ، نحتاج إلى كائن من شأنه تخزين وسيطات الوظائف مؤقتًا ، وكذلك تشغيل الوظيفة عندما يُطلب منه ذلك يتحول إلى نوع الإرجاع المطلوب:
#include<tuple>
template<size_t num_args, typename ...T>
class Foo;
template<typename ...T>
class Foo<2,T...> : public std::tuple<T&&...>
{
public:
Foo(T&&... args) :
std::tuple<T&&...>(std::forward<T>(args)...)
{}
template< typename Return >
operator Return() { return foo<Return>(std::get<0>(*this), std::get<1>(*this)); }
};
template<typename ...T>
class Foo<3,T...> : std::tuple<T&&...>
{
public:
Foo(T&&... args) :
std::tuple<T&&...>(std::forward<T>(args)...)
{}
template< typename Return >
operator Return() { return foo<Return>(std::get<0>(*this), std::get<1>(*this), std::get<2>(*this)); }
};
template<typename ...T>
auto
auto_foo(T&&... args)
// -> Foo<T&&...> // old, incorrect, code
-> Foo< sizeof...(T), T&&...> // to count the arguments
{
return {std::forward<T>(args)...};
}
أيضًا ، يعمل ما ورد أعلاه لوظائف ARG أو ثلاثية ARG ، ليس من الصعب معرفة كيفية تمديد ذلك.
هذا هو الكثير من التعليمات البرمجية للكتابة! لكل وظيفة ستقوم بتطبيقها على ذلك ، يمكنك كتابة ماكرو يقوم بذلك نيابة عنك. شيء من هذا القبيل في الجزء العلوي من ملفك:
REGISTER_FUNCTION_FOR_DEDUCED_RETURN_TYPE(foo); // declares
// necessary structure and auto_???
وبعد ذلك يمكنك استخدامه auto_foo
في برنامجك.
بنفس الطريقة لا يمكنك التحميل الزائد في نوع الإرجاع ، لا يمكنك إجراء خصم القالب عليه. وللسبب نفسه - إذا كان f () هو قالب/تحميل زائد يرجع شيء ما ، فما النوع الذي يجب استخدامه هنا:
f();
يمكنك محاولة استخدام ماكرو لذلك. بخلاف ذلك ، لا أرى كيف من المفترض أن يعمل ببيان واحد فقط.
#define ALLOC(ptrname,type) GCPtr<type> ptrname = GC::Allocate<type>()
ALLOC(ptr1,A);
نقاط يوهانس صالحة. يتم إصلاح المشكلة >> بسهولة. لكنني أعتقد أن وجود فواصل كجزء من النوع يتطلب امتداد C99 Preprocessor Varargs:
#define ALLOC(ptrname,...) GCPtr< __VA_ARGS__ > ptrname = GC::Allocate< __VA_ARGS__ >()
ALLOC(ptr1,SomeTemplate<int,short>);