【问题标题】:Function signature-like expressions as C++ template arguments类似函数签名的表达式作为 C++ 模板参数
【发布时间】:2011-06-06 05:43:31
【问题描述】:

我正在查看 Don Clugston 的 FastDelegate 迷你库,并注意到一个奇怪的语法技巧,其结构如下:

TemplateClass< void( int, int ) > Object;

看起来好像函数签名被用作模板实例声明的参数。

这种技术(其在 FastDelegate 中的出现显然是由于一个 Jody Hagins)被用来简化模板实例的声明,其中包含半任意数量的模板参数。

也就是说,它允许这样的事情:

// 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;

尽管有真正的骇人听闻的味道,但我发现这种对可变参数模板参数的恶搞模拟非常令人兴奋。

这个技巧的真正意义似乎在于我可以将诸如“Type1(Type2, Type3)”之类的文本结构作为参数传递给模板。所以这是我的问题:编译器究竟是如何解释这个结构的?它是函数签名吗?或者,它只是一个带有括号的文本模式?如果是前者,那么这是否意味着就模板处理器而言,任意函数签名都是有效类型?

一个后续问题是,既然上面的代码示例是有效代码,为什么 C++ 标准不允许您执行以下无法编译的操作?

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;

【问题讨论】:

标签: c++ templates


【解决方案1】:

关于您的第一个问题 - 关于类型 int(char, float) - 这是一个有效的 C++ 类型,并且是一个函数的类型,它接受 charfloat 并返回 int。请注意,这是实际函数的类型,而不是函数指针,它是int (*) (char, float)。任何函数的实际类型都是这种不寻常的类型。例如,类型

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

void ()

在例行编程中很少出现这种情况的原因是,在大多数情况下,您不能声明这种类型的变量。例如,这段代码是非法的:

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

但是,您确实看到使用的函数类型的一种情况是传递函数指针:

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

这种写成函数指针的函数比较常见,但函数本身就可以了。它在幕后进行。

至于你的第二个问题,为什么用不同数量的参数编写相同的模板是非法的,我不知道规范中禁止它的确切措辞,但这与这样一个事实有关你已经声明了一个类模板,你不能改变它的参数数量。但是,您可以在具有不同数量参数的模板上提供部分特化,当然前提是部分特化只对原始数量的参数进行特化。例如:

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

这里,Function 总是采用一个参数。模板特化有两个参数,但特化仍然只有一种类型(具体来说,Ret (Arg))。

【讨论】:

  • 非常漂亮。函数“类型”的想法几乎超出了日常 C++ 概念空间,这就是为什么这个技巧起初对我来说很奇怪。但是,如果“void(int)”是一种类型,是否可以拥有例如指向“void(int)”的指针的类型?这和函数指针是一样的吗?
  • 啊哈,因此“正确”的函数指针语法,例如“无效 (*fp)() = & SomeFunction;”。还有取消引用的函数调用“(*fp)();”在我看来,有些编译器允许你放弃地址和取消引用运算符……这让函数类型和函数指针类型看起来像一回事,而它们显然不是。
  • @Jeff Lee- 编译器实际上必须让你摆脱这个问题。 C++ ISO 规范(第 4.3.1 节)提到任何函数都可以隐式转换为指向该函数的指针。我认为这是为了 C 兼容性。
  • @templatetypedef - 这是否与 Clugston 在链接文章中的声明相矛盾,或者他是否在谈论其他内容:“一些编译器(最显着的是 MSVC 6 和 7)会让你省略 &,即使它是非标准且令人困惑的。更多符合标准的编译器(例如,GNU G++ 和 MSVC 8(又名 VS 2005))需要它,所以你一定要把它放进去。”
  • 在原始函数的上下文中,您不需要 &,但对于指向成员函数的指针,& 是必需的。我不确定克拉格斯顿在那篇文章中的意思,但似乎他有点误会了。只是猜测。
【解决方案2】:
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.
};

【讨论】:

  • 对,这绝对是正确的。尽管在我的示例中,将函数签名作为模板参数的声明不是 Object1。相反,声明是通过一个名为“Object”的派生类过滤的,它从签名中提取单个类型。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-11-18
  • 2015-07-29
相关资源
最近更新 更多