如何正确声明将函数类型作为参数的模板(如 std::function)

how to declare properly the template taking function type as a parameter (like a std::function)

我发现像下面这样的简洁语法并不简单:

std::function<int(float, bool)>

如果我将函数声明为:

template <class RetType, class... Args>
class function {};

定义函数模板类型的普通语法:

function<int,float,bool> f;

但它使用部分模板特化的奇怪技巧

template <class> class function; // #1

template <class RV, class... Args>
class function<RV(Args...)> {} // #2

这是为什么? 为什么我需要给模板一个带有空类型参数 (#1) 的空一般形式,否则它就不会编译

这就是语言的设计方式。主模板不能像这样对类型进行复杂的分解;您需要使用部分专业化。

如果我没理解错的话,你想只写第二个版本而不必提供主模板。但是想想模板参数如何映射到模板参数:

template <class RV, class Arg1, class... Args>
class function<RV(Arg1, Args...)> {}

function<int(float,bool)>; //1
function<int, float, bool>; //2

Option 1 是你要写的,但是注意你传递的是一个单一的函数类型给一个模板,其中的参数是两个类型参数和一个类型参数包。换句话说,在没有主模板的情况下编写此代码意味着您的模板参数不一定与模板参数匹配。选项 2 匹配模板参数,但不匹配专业化。

如果你有不止一种专业,这就更没有意义了:

template <class RV, class Arg1, class... Args>
class function<RV(Arg1, Args...)> {}

template <class T, class RV, class Arg1, class... Args>
class function<RV (T::*) (Arg1, Args...)> {}

您也许可以想出一些规则来从专业化中推断出主要模板,但这对我来说似乎很糟糕。

您必须记住 int (float, bool) 类型 。更准确地说,它是类型 "function which takes two parameters of type float and bool, and returns int."

因为它是一个类型,很明显模板必须有一个类型参数在你想使用语法的地方int (float, bool).

同时,只有函数类型是笨拙的。当然,你可以轻松做到。例如,如果您需要做的只是某种转发器:

template <class T>
struct CallForwarder
{
  std::function<T> forward(std::function<T> f)
  {
    std::cout << "Forwarding!\n";
    return f;
  }
};

但是,一旦要访问函数类型的"components",就需要一种方法来为它们引入标识符。最自然的方法是函数类型的部分特化,就像您在问题中所做的那样(就像 std::function 所做的那样)。

"Why I need to give the template an empty general form with empty type parameter"

因为,你想要在 "return type" 和 "argument types" 之间进行明确的类型区分,具有类似语法糖的清晰度。在 C++ 中,template class 的第一手版本没有这样的语法。你必须专门化它,才能区分它。

看完你的问题后,我查看了 <functional> header 文件。 甚至 std::function 也做同样的事情:

// <functional>  (header file taken from g++ Ubuntu)

template<typename _Signature>
class function;

// ...

 /**
   *  @brief Primary class template for std::function.
   *  @ingroup functors
   *
   *  Polymorphic function wrapper.
   */
template<typename _Res, typename... _ArgTypes>
class function<_Res(_ArgTypes...)>

正如您在他们的评论中所见,他们将专用版本视为 "Primary" class。因此这个技巧来自标准 header 本身!

你实际上可以这样做:

template <typename T>
class X { };

X<int(char)> x;

X 的定义中,您可以像创建 std::function<int(char)> 一样创建 std::function<T>。这里的问题是您不能(至少很容易)访问 return 类型和参数的参数类型(此处为 intchar)。

使用 "trick",您可以毫无问题地访问它们 - 它 "simply" 让您的代码更清晰。