当前位置: 首页 > 知识库问答 >
问题:

为什么在函数参数类型中使用模板参数包作为其模板参数列表,而不能显式指定

狄德泽
2023-03-14

我有以下代码:

template <typename, typename>
struct AAA{};

template<typename ...Args>
void f(AAA<Args...> *) {}

int main() {
    f<int, int>(nullptr);
}

这段代码会导致编译错误。使用< code>g -std=c 1z编译时,错误显示如下:

prog.cc: In function 'int main()':
prog.cc:8:24: error: no matching function for call to 'f<int, int>(std::nullptr_t)'
     f<int, int>(nullptr);
                        ^
prog.cc:5:6: note: candidate: template<class ... Args> void f(AAA<Args ...>*)
 void f(AAA<Args...> *) {}
      ^
prog.cc:5:6: note:   template argument deduction/substitution failed:
prog.cc:8:24: note:   mismatched types 'AAA<Args ...>*' and 'std::nullptr_t'
     f<int, int>(nullptr);

使用< code>clang -std=c 1z时,错误为:

prog.cc:8:5: error: no matching function for call to 'f'
    f<int, int>(nullptr);
    ^~~~~~~~~~~
prog.cc:5:6: note: candidate template ignored: could not match 'AAA<int, int, Args...> *' against 'nullptr_t'
void f(AAA<Args...> *) {}
     ^
1 error generated.

我在MSYS2 MinGW-w64环境中运行这些。我的GCC版本是GCC 7.1.0,我的Clang版本是4.0.0;我在GCC和Clang中使用的标准库是与我的GCC编译器捆绑在一起的libstdc。

在我看来,对函数template foo的调用已经明确指定了模板参数,因此模板参数包和函数参数类型应该已经指定。但是,上面显示的错误诊断似乎表明函数参数的确切类型与< code>nullptr参数不匹配,这似乎是一个只有在函数参数演绎发生时才可能出现的问题。所以我的问题是,为什么会出现这样的错误?这只是一个编译器错误,还是C标准有一些规则表明原始代码只是格式不良?

共有3个答案

澹台玉石
2023-03-14

只需添加一个简单的解决方案

f<int, int>(nullptr); // doesn't work for the reasons explained by other answers
(*f<int, int>)(nullptr); // OK - does what you want

后者强制 Pack Args... 成为 {int, int},现在调用本身不是对函数模板的调用 - 它只是对函数指针的调用。我们调用的是一个采用 AAA 的函数

为了好玩,您还可以添加任意多个*s:

(*****f<int, int>)(nullptr); // still OK - does what you want

...但是,你知道...不要。

上官凯泽
2023-03-14

因为您正在使用typename... Args,编译器不知道int, int是否是所有正在使用的模板参数,或者通过推导函数参数可以使用更多参数。因此,函数尚未实例化,编译器继续尝试从函数参数中推导参数包的所有其他可能参数。

换句话说,这是可行的:

f<int>(new AAA<int, int>);

因为您说第一个参数是< code>int,但是编译器需要一个参数列表,并继续尝试贪婪地从函数实参中找到越来越多的参数,然后它实例化函数模板。

在你的案例中或多或少也会发生同样的情况,但是编译器无法从nullptr_t推断出函数参数不匹配的任何内容。它需要指向 A 的指针

template <typename, typename>
struct AAA{};

template<typename A, typename B>
void f(AAA<A, B> *) {}

int main() {
    f<int, int>(nullptr);
}

因为编译器知道模板参数是两个,并且您提供了所有的参数,所以没有什么可推导的,函数可以被实例化。这也更有意义,因为< code>AAA只接受两个模板参数,所以< code>f的参数包在这里似乎没有用。

锺离嘉容
2023-03-14

您可能认为编译器应该将包推断为< code>int,int,但是C标准明确要求您观察到的行为。

[temp.arg.explicit/9]

模板参数演绎可以扩展对应于模板参数包的模板参数序列,即使该序列包含显式指定的模板参数。[示例:

template<class ... Types> void f(Types ... values);
void g() {
  f<int*, float*>(0, 0, 0);     // Types is deduced to the sequence int*, float*, int
}

—示例结束]

上面的意思是,即使指定了一些参数,演绎也不会结束。参数包必须始终可以通过参数推导来扩展。这就好像给定的显式参数是带有尾随参数包的模板的实例化。当结合以下因素时:

[温度参数显式/3]

可以从显式模板参数列表中省略从默认模板参数推导或获得的后续模板参数。未以其他方式推导的尾部模板参数包将被推导为模板参数的空序列。。。

编译器必须将未推导的参数与空包匹配。但它没有什么可推断的。

因此,您将Args...插入AAA的尝试不可能匹配。因为类型序列是带有尾随列表的两种类型(编译器无法从nullptr推断为空)。而AAA只需要两种类型。

 类似资料:
  • 考虑以下示例: 我的GCC 9.2.0无法编译并出现以下错误: 但是,工作正常。为什么会这样?如何使用显式模板参数调用foo?

  • 我目前有一个,但是为了灵活性,我希望能够分配一个lambda表达式,将作为映射中的值返回。 所以我创建了这个模板类: 并像这样使用它: IntelliSense提供了更多信息: 多个操作符“=”匹配这些操作数:function“valueorfunction::operator=(const std::function&other)[with T=std::wstring]”function“va

  • 如果我没有理解错的话,类模板定义了一个函数,所以当我调用时,编译器有可能进行隐式强制转换,但是在函数模板的情况下,此时没有函数定义,所以隐式强制转换不会发生。 但我不明白为什么编译器不能创建函数定义,然后应用隐式强制转换? 错误是: 在函数“int main()”中:    25:24:错误:调用“test2::add(void(&)(int))”没有匹配函数    25:24:注:候选人是:  

  • 在里面https://github.com/stlab/libraries/blob/main/stlab/concurrency/main_executor.hpp,我读到 decltype(f)的意义是什么,为什么不直接使用f?