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

可变函数中可变模板类的隐式转换

李胤
2023-03-14

考虑下面的代码

#include <functional>
template<class ResultType, class ... Args>
void Foo( std::function<ResultType(Args...)> ) {}

void Dummy(int) {}

int main()
{
     Foo<void, int> ( std::function<void(int)>( Dummy ) ); // OK, no deduction and no conversion
     Foo( std::function<void(int)>( Dummy ) ); // OK, template argument deduction
     Foo<void, int>( Dummy ); // Compile error
}

在第三个例子中,我知道模板推导不能发生,这就是为什么要显式指定模板参数。但是为什么没有从void(*)(int)std::function的显式转换呢

我查找了答案,但这些都是关于模棱两可的重载解析或模板演绎,而不是问题的主题。

难道模板参数(签名)的std::function的一部分类型?

使用std::函数进行模板类型推断

使用std::函数的隐式转换

然后我尝试用我自己的模板类而不是std::function进行测试。

// Variadic template class
template<class ... T>
class Bar
{
public:
    // Non-explicit ctor, an int can go through implicit conversion
    Bar(int) {}
};

// A template function
template<class T>
void Xoo( Bar<T> ) {}

// Same, but this one has a variadic template
template<class ... T>
void Yoo( Bar<T...> ) {}

int main()
{
    Xoo( Bar<bool>( 100 ) ); //OK, argument deduction
    Xoo<bool>( 100 ); //OK, implicit conversion
    Yoo( Bar<bool>( 100 ) ); //OK, argument deduction
    Yoo<bool>( 100 ); // Not ok... ?
}

GCC 9.2.0的输出

prog.cc: In function 'int main()':
prog.cc:23:19: error: no matching function for call to 'Yoo<bool>(int)'
   23 |    Yoo<bool>( 100 ); // Not ok... ?
      |                   ^
prog.cc:16:6: note: candidate: 'template<class ... T> void Yoo(Bar<T ...>)'
   16 | void Yoo( Bar<T...> ) {}
      |      ^~~
prog.cc:16:6: note:   template argument deduction/substitution failed:
prog.cc:23:19: note:   mismatched types 'Bar<T ...>' and 'int'
   23 |    Yoo<bool>( 100 ); // Not ok... ?
      |                   ^

clang 9.0.0的输出

prog.cc:23:4: error: no matching function for call to 'Yoo'
   Yoo<bool>( 100 ); // Not ok... ?
   ^~~~~~~~~
prog.cc:16:6: note: candidate template ignored: could not match 'Bar<bool, type-parameter-0-0...>' against 'int'
void Yoo( Bar<T...> ) {}
     ^
1 error generated.

为什么,如果函数具有可变模板,则不会进行隐式转换(即使显式指定了模板参数)?我回到std::function,果然,如果该函数没有可变模板,它就可以工作。

#include <functional>
// Not variadic this time
template<class ResultType, class Arg>
void Goo( std::function<ResultType(Arg)> ) {}
void Dummy(int) {}
int main()
{
     Goo<void, int> ( Dummy ); // Ok this time
}

有趣的是,下面的修改使它以铿锵的方式编译

[...]

// Same, but this one has a variadic template
template<class ... T>
void Yoo( Bar<T..., bool> ) {}
//                  ^^^^
// An extra template for Bar makes implicit conversion 
// work for some reason

[...]

我试图寻找更多与变量模板相关的答案,但不是关于这个特定的主题,就是在这一点上我无法理解。

当变量模板不是最后一个参数时,如何重载它们

不作为最后一个参数传递时的模板参数包推导

带可变模板构造函数的演绎指南和可变类模板-参数包长度不匹配

std::function参数的模板参数和推导

带可变模板构造函数的演绎指南和可变类模板-参数包长度不匹配


共有1个答案

东门彬
2023-03-14
template<class ResultType, class ... Args>
void Foo( std::function<ResultType(Args...)> ) {}

Foo<void, int> ( std::function<void(int)>( Dummy ) ); // OK, no deduction and no conversion
Foo( std::function<void(int)>( Dummy ) ); // OK, template argument deduction
Foo<void, int>( Dummy ); // Compile error

Foo

您认为它显式地将模板参数指定为Foo。它实际上做的是声明Foo的模板参数以space开始,然后是int,然后...然后它什么也没说。

所以模板参数推导仍然运行,以找出其余的参数是什么。它失败了。然后你的编译器会抱怨。

看到这个

 Foo<void, int> ( std::function<void(int,int)>( nullptr) );

你会看到我们传入了void,int,但是我们推断出的是void,int,int——两个int不是一个。

...

对于您的特定问题,您正在将模板参数推导与类型擦除类型(std::function)混合使用,并且这两种操作都很像是在车上喷漆,因为您想剥掉油漆。

模板参数推导和类型擦除操作是相互不完全逆的。

当剩下一个变量包时,一旦传递了每个参数,就不需要进行演绎,所以它不再进行模板参数演绎。

对于

如果您真的想这样做,您可以:

template<class T>
struct identity { using type=T; };
template<class T> using identity_t = typename identity<T>::type;

template<class ResultType, class ... Args>
void Foo( identity_t<std::function<ResultType(Args...)>> ) {}

这也阻止了模板参数推导。

现在呢

Foo( std::function<void(int)>( Dummy ) ); // OK, template argument deduction

不起作用,因为它拒绝推断论点。

您可以通过一些愚蠢的行为来支持这两个方面:

template<class ResultType, class ... Args>
void Foo( identity_t<std::function<ResultType(Args...)>> ) {}

struct never_use {};
template<class R0=never_use, class ResultType, class ... Args>
requires (std::is_same_v<R0, never_use>)
void Foo( std::function<ResultType(Args...)> ) {}

但这不支持在不进行更愚蠢的操作的情况下传递部分参数。

 类似资料:
  • 在C++11之前,类模板和函数模板只能含有固定数量的模板参数。C++11增强了模板功能,允许模板定义中包含0到任意个模板参数,这就是可变参数模板。可变参数模板的加入使得C++11的功能变得更加强大,而由此也带来了许多神奇的用法。 可变参数模板 可变参数模板和普通模板的语义是一样的,只是写法上稍有区别,声明可变参数模板时需要在typename或class后面带上省略号...: template<ty

  • 我试图理解下面代码中的编译器错误。我有一个variadic模板函数,它接受具有指定类型的lambda,试图调用该函数会导致模板由于不匹配而不被视为有效的候选模板。 如果我将声明更改为不变: 然后它为上面的玩具示例工作,但对于真正的问题,我需要任意的论点。我是不是缺少了什么,或者还有什么方法可以做到这一点? 编辑:这被错误地标记为重复,我相信--受骗者没有回答我所问的问题。这个问题特别与这里的var

  • 受这个答案的启发,我生成了这段代码,其输出取决于编译器: 如果使用 GCC 11 编译,调用

  • 我希望打印函数根据“值”的类型来做不同的事情。

  • 有了C++20和概念,我想知道是否可以写一个概念来检查一个类型是否有一个带有特定名称的函数,该函数接受任意数量的任意参数。 以下面的代码为例(使用GCC当前的概念TS语法):

  • 我有两个变量类成员函数。当第一个被调用时,我想创建一个std::function到第二个类成员函数,然后将的参数绑定到函数指针。 所以稍后我可以调用,而不必将所有参数传递给 我想避免使它成为模板类,并将参数存储在元组中。 我试图让以下示例起作用: 链接到live示例:http://cpp.sh/4ylm 当我编译时,我得到一个错误 模板参数推断/替换失败:17:37: 注意:无法推断模板参数“\u