类似函数签名的表达式作为C ++模板参数 [英] Function signature-like expressions as C++ template arguments
问题描述
我在看看Don Clugston的 FastDelegate 迷你库,并发现了一个奇怪的语法技巧,包含以下内容结构:
TemplateClass< void(int,int)>目的;
它几乎看起来好像函数签名被用作模板实例声明的参数。 / p>
这种技术(其在FastDelegate中的存在显然是由于一个Jody Hagins)用于简化具有半任意数量的模板参数的模板实例的声明。 p>
也就是说,它允许这样的东西:
具有一个参数的模板
template< typename _T1>
struct Object1
{
_T1 m_member1;
};
//具有两个参数的模板
模板< typename _T1,typename _T2>
struct Object2
{
_T1 m_member1;
_T2 m_member2;
};
//前向声明
模板< typename _Signature>
struct Object;
//使用函数签名的一些派生类型 - 样式模板参数
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对象声明
Object1< int> IntObjectA;
Object2< int,char> IntCharObjectA;
// B. Nifty,但等价的对象声明
typedef void UnusedType;
Object< UnusedType(int)> IntObjectB;
Object< UnusedType(int,char)> IntCharObjectB;
// C.甚至niftier,仍然等价的对象声明
#define DeclareObject(...)Object< UnusedType(__VA_ARGS__)>
DeclareObject(int)IntObjectC;
DeclareObject(int,char)IntCharObjectC;
我发现这种欺骗性的仿真模型参数令人兴奋。
这种骗术的真正原因似乎是我可以通过文本结构像Type1(Type2,Type3)作为模板的参数的事实。所以这里是我的问题:编译器如何解释这个结构?它是一个函数签名吗?或者,它只是一个带括号的文本模式吗?如果是前者,则这是否意味着对于模板处理器,任何任意函数签名是有效类型?
后续问题是,上面的代码示例是有效的代码,为什么C ++标准不允许你做类似下面的事情,不能编译?
template< typename _T1>
struct Object
{
_T1 m_member1;
};
//注意类标识符也是Object
template< typename _T1,typename _T2>
struct Object
{
_T1 m_member1;
_T2 m_member2;
};
Object< int> IntObject;
Object< int,char> IntCharObject;
关于你的第一个问题 - code> int(char,float) - 这是一个有效的C ++类型,是一个接受 char
和 float
,并返回 int
。注意,这是实际函数的类型,而不是函数指针,它将是 int(*)(char,float)
。任何功能的实际类型是这种不寻常的类型。例如,类型
void DoSomething(){
/ * ... * /
}
是 void()
p>
在常规编程中不会出现这种情况的原因是在大多数情况下你不能声明这种类型的变量。例如,此代码是非法的:
void MyFunction(){
void function()= DoSomething; //错误!然而,有一种情况,你确实看到使用的函数类型是用于传递函数的函数类型(例如:
}
指针:
void MyFunction(void FunctionArgument()){
/ * ... * /
}
更常见的是看到这种函数写入一个函数指针,完美地接受函数本身。
至于你的第二个问题,为什么使用不同数量的参数编写相同的模板是非法的,我不知道在规范中的准确的措辞,禁止它,但它与一个事实,一旦你宣布一个类模板,你不能改变参数的数量。但是,您可以对具有不同数量参数的模板提供部分专门化,当然,部分专门化仅专用于原始数量的参数。例如:
template< typename T>类函数;
template< typename Arg,typename Ret>类Function< Ret(Arg)> {
/ * ... * /
};
这里,函数
总是带有一个参数。模板特化包含两个参数,但专业化仍然只有一种类型(具体来说, Ret(Arg)
)。
I was looking at Don Clugston's FastDelegate mini-library and noticed a weird syntactical trick with the following structure:
TemplateClass< void( int, int ) > Object;
It almost appears as if a function signature is being used as an argument to a template instance declaration.
This technique (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.
To wit, it allowed this something like 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 whiff of hackiness, I find this kind of spoofy emulation of variadic template arguments to be pretty mind-blowing.
The real meat of this trick seems to be the fact that I can pass textual constructs like "Type1(Type2, Type3)" as arguments to templates. So here are my questions: How exactly does the compiler interpret this construct? Is it a function signature? Or, is it just a text pattern with parentheses in it? If the former, then does this imply that any arbitrary function signature is a valid type as far as the template processor is concerned?
A follow-up question would be that since the above code sample is valid code, why doesn't the C++ standard just allow you to do something like the following, which 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;
With regards to your first question - about the type int(char, float)
- this is a valid C++ type and is the type of a function that takes in a char
and a float
and returns an int
. Note that this is the type of the actual function, not a function pointer, which would be an int (*) (char, float)
. The actual type of any function is this unusual type. For example, the type of
void DoSomething() {
/* ... */
}
is void ()
.
The reason that this doesn't come up much during routine programming is that in most circumstances you can't declare variables of this type. For example, this code is illegal:
void MyFunction() {
void function() = DoSomething; // Error!
}
However, one case where you do actually see function types used is for passing function pointers around:
void MyFunction(void FunctionArgument()) {
/* ... */
}
It's more common to see this sort of function written to take in a function pointer, but it's perfectly fine to take in the function itself. It gets casted behind-the-scenes.
As for your second question, why it's illegal to have the same template written with different numbers of arguments, I don't know the exactly wording in the spec that prohibits it, but it has something to do with the fact that once you've declared a class template, you can't change the number of arguments to it. However, you can provide a partial specialization over that template that has a different number of arguments, provided of course that the partial specialization only specializes over 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. The template specialization takes in two arguments, but the specialization is still only over one type (specifically, Ret (Arg)
).
这篇关于类似函数签名的表达式作为C ++模板参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!