Functional signature expressions as arguments to a C ++ template

I looked at the Don Clugston FastDelegate mini library and noticed a strange syntax trick with the following structure:

TemplateClass< void( int, int ) > Object; 

It looks like the function signature is used as an argument to declare an instance of the template.

This method (whose presence in FastDelegate is apparently due to one Jody Hagins) was used to simplify the declaration of template instances with a semi-arbitrary number of template parameters.

In particular, this allowed us to do the following:

 // A template with one parameter template<typename _T1> struct Object1 { _T1 m_member1; }; // A template with two parameters template<typename _T1, typename _T2> struct Object2 { _T1 m_member1; _T2 m_member2; }; // A forward declaration template<typename _Signature> struct Object; // Some derived types using "function signature"-style template parameters template<typename _Dummy, typename _T1> struct Object<_Dummy(_T1)> : public Object1<_T1> {}; template<typename _Dummy, typename _T1, typename _T2> struct Object<_Dummy(_T1, _T2)> : public Object2<_T1, _T2> {}; // A. "Vanilla" object declarations Object1<int> IntObjectA; Object2<int, char> IntCharObjectA; // B. Nifty, but equivalent, object declarations typedef void UnusedType; Object< UnusedType(int) > IntObjectB; Object< UnusedType(int, char) > IntCharObjectB; // C. Even niftier, and still equivalent, object declarations #define DeclareObject( ... ) Object< UnusedType( __VA_ARGS__ ) > DeclareObject( int ) IntObjectC; DeclareObject( int, char ) IntCharObjectC; 

Despite the real smell of hacking, I find that such a fake emulation of the template's variational arguments is quite captivating.

The real meat of this trick seems to be the fact that I can pass text constructs like "Type1 (Type2, Type3)" as template arguments. So, here are my questions: how exactly does the compiler interpret this construct? Is this a function signature? Or is it just a text template with parentheses? If the former, then does this mean that any arbitrary function signature is a valid type with respect to the template processor?

The next question will be that, since the above code example is valid code, why the C ++ standard does not allow you to do something like the following that does not compile?

 template<typename _T1> struct Object { _T1 m_member1; }; // Note the class identifier is also "Object" template<typename _T1, typename _T2> struct Object { _T1 m_member1; _T2 m_member2; }; Object<int> IntObject; Object<int, char> IntCharObject; 
+43
c ++ templates
Jan 09 2018-11-11T00:
source share
2 answers

As for your first question - about type int(char, float) - this is a valid C ++ type and is a type of function that takes char and float and returns int . Note that this is the type of the actual function, not the function pointer, which would be int (*) (char, float) . The actual type of any function is an unusual type. For example, type

 void DoSomething() { /* ... */ } 

- void () .

The reason why this largely does not occur during normal programming is that in most cases you cannot declare variables of this type. For example, this code is illegal:

 void MyFunction() { void function() = DoSomething; // Error! } 

However, one case where you really see the types of functions used is to pass function pointers around:

 void MyFunction(void FunctionArgument()) { /* ... */ } 

It is more common to see that this kind of function is written to the function pointer, but it is great for the function itself. He is cast behind the scenes.

As for your second question, why is it illegal to have the same template written with different numbers of arguments, I don’t know the exact wording in the specification that prohibits it, but it has something to do with the fact that once you declared a class template, you cannot change the number of arguments. However, you can provide partial specialization for this template, which has a different number of arguments, provided, of course, that partial specialization specializes only in the original number of arguments. For example:

 template <typename T> class Function; template <typename Arg, typename Ret> class Function<Ret (Arg)> { /* ... */ }; 

Here, Function always takes one parameter. Template specialization takes two arguments, but specialization still exists over only one type (in particular, Ret (Arg) ).

+38
Jan 09 '11 at 10:00
source share
 int* int_pointer; // int_pointer has type "int*" int& int_reference; // int_reference has type "int&" int int_value; // int_value has type "int" void (*function_pointer)(int, int); // function_pointer has type // "void (*)(int, int)" void (&function_reference)(int, int); // function_reference has type // "void (&)(int ,int)" void function(int, int); // function has type // "void(int, int)" template<> struct Object1<void(int, int)> { void m_member1(int, int); // wait, what?? not a value you can initialize. }; 
+2
Jan 09 '11 at 10:01
source share



All Articles