这个语法 `class template <class R, class ...Args> class name<R(Args...)>` 是什么意思?

6

我一直在尝试了解C++中的多线程编程,并且我很难理解 std::promise,所以我开始在这个网站上寻找答案。没想到,有人和我有同样的问题。但是读完答案后,我更加困惑了。

以下代码应该是类似于std::packaged_task的实现:

template <typename> class my_task;

template <typename R, typename ...Args>
class my_task<R(Args...)>
{
    std::function<R(Args...)> fn;
    std::promise<R> pr;             // the promise of the result
public:
    template <typename ...Ts>
    explicit my_task(Ts &&... ts) : fn(std::forward<Ts>(ts)...) { }

    template <typename ...Ts>
    void operator()(Ts &&... ts)
    {
        pr.set_value(fn(std::forward<Ts>(ts)...));  // fulfill the promise
    }

    std::future<R> get_future() { return pr.get_future(); }

    // disable copy, default move
};

在这段代码中,
1- 这个语法 template <typename R, typename ...Args> class my_task<R(Args...)> 是什么意思?更具体地说,<R(Args...)> 的目的是什么?
2- 为什么需要对该类进行前向声明?
谢谢。

4
哇哦,每个问题请只提一个问题。 - Lightness Races in Orbit
我应该将它分成三个不同的问题吗?它们似乎密切相关,特别是第一和第二个。 - GamefanA
是的,我认为是这样。你在这里有三个相当不错的问题的开端,但它们不应该被合并成一个,而且还需要一些充实和研究。相关并不意味着完全相同。 - Lightness Races in Orbit
我想要研究它们,但我不知道该搜索什么,我无法在谷歌上准确地输入“class template <class R, class …Args> class name<R(Args…)>”,因为它不知道我在说什么。 - GamefanA
1
这可能会有所帮助 https://dev59.com/4m445IYBdhLWcg3w6eNo - Pradhan
谢谢Pradhan,这真的很有帮助。 - GamefanA
2个回答

5

在评论中有一些关于1和2应该是两个独立问题的讨论,但我认为它们只是同一个问题的两个方面,原因如下:

template <typename> class my_task;

template <typename R, typename ...Args>
class my_task<R(Args...)>; ....

第一条语句声明了一个模板,该模板以typename为唯一模板参数。第二条语句声明了该模板类的一个特化。

在这个上下文中:

 R(Args...)

将专门为任何与函数匹配的typename进行特化。此模板特化将匹配传递函数签名作为typename的任何模板实例化。除了模板本身存在问题外,此模板特化将用于:

 my_task<int (const char *)>

或者,一个接受const char *参数并返回int的函数。模板特化也将匹配:

 my_task<Tptr *(Tptr **, int)>

或者,一个接受两个参数的函数,Tptr **int,并返回一个 Tptr * (这里,Tptr 是另一个类)。
模板特化将不匹配:
 my_task<int>

或者

 my_task<char *>

由于它们不是函数签名。如果您尝试使用非函数typename实例化此模板,将会出现编译错误。为什么呢?

嗯,那是因为该模板未定义:

template<typename> class my_task;

不要把它看作仅仅是前置声明。它是一个带有模板参数的模板的前置声明,而且该模板不会在任何地方定义。相反,模板声明允许对后续的特化声明进行限制,只匹配传递为模板参数的特定类型。
这是一种常见的编程技巧,用于限制可以与特定模板一起使用的typename或class的种类。模板不能与任何typename或class一起使用,而只能与其中的某些子集一起使用。在这种情况下,是函数typename或签名。
这也使得模板本身更容易明确地引用——在这种情况下——模板参数的返回类型和参数类型。如果模板只有一个平淡无奇的单一typename作为模板参数,则很难访问函数的返回类型或函数参数的类型。

非常感谢,现在一切都清楚了。我知道这是一种特殊的语法,但我以前从未见过这样的语法。 - GamefanA

2

1: 这个语法 template <typename R, typename ...Args> class my_task<R(Args...)> 是什么意思?

这是类模板 my_task 的一个特化。名称后面的 <R(Args...)> 表示它是针对该类型进行特化,而且该类型是一个函数。 R(Args...) 是一个带有 Args 参数并返回 R 的函数类型。因此,例如 my_task<void()> mt; 将使 Args 成为一个空参数包,并且 R 将为 void

2: 为什么需要对该类进行前向声明?

该类已经被声明,但与普通的前向声明不同,未特化版本没有被定义。这个类只适用于当类型是一个函数时,如果有人尝试使用不是函数的东西(如 my_task<int>),它将会报错,指出该类型未被定义。

my_task<void*(int, int)> mt1; //R = void*, Args = int, int
my_task<int> mt2; //error: use of undefined class

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接