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

P. *_*uez 5 c++ templates variadic-functions implicit-conversion variadic-templates

考虑以下代码

#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
}
Run Code Online (Sandbox Code Playgroud)

在第三个中,我知道不能进行模板推导,这就是明确指定模板参数的原因。但是为什么没有从void (*)(int)to的显式转换std::function<void(int)>

我查找了答案,但这些是关于模棱两可的重载解决方案或模板推论,而不是相关主题。

std::function 的模板参数(签名)不是其类型的一部分吗?

使用 std::function 进行模板类型推导

使用 std::function 进行隐式转换

然后我尝试使用我自己的模板类而不是 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... ?
}
Run Code Online (Sandbox Code Playgroud)

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... ?
      |                   ^
Run Code Online (Sandbox Code Playgroud)

来自 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.
Run Code Online (Sandbox Code Playgroud)

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

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... ?
      |                   ^
Run Code Online (Sandbox Code Playgroud)

有趣的是,以下修改使它在clang中编译

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.
Run Code Online (Sandbox Code Playgroud)

我试图寻找更多与可变参数模板相关的答案,但要么没有关于这个特定主题,要么在这一点上我无法理解。

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

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

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

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

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

Yak*_*ont 1

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
Run Code Online (Sandbox Code Playgroud)

Foo<void,int>不做你认为它做的事。

您认为它明确指定了 的模板参数Foo。它实际上所做的是声明模板参数以Foo then void , intand ... 开头,然后什么也没说。

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

看到这个

 Foo<void, int> ( std::function<void(int,int)>( nullptr) );
Run Code Online (Sandbox Code Playgroud)

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

...

对于您的特定问题,您将模板参数推导与类型擦除类型 ( std::function) 混合在一起,并且同时执行这两个操作很像给汽车喷漆,因为您想剥掉油漆。

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

当剩下一个变量包时,一旦传递了每个参数,就不需要进行推导,因此它不再进行模板参数推导。

在你的情况下,Bar<T...,bool>你正在阻止参数推导,因为当包后面有任何东西时,C++ 拒绝推导包。

如果你真的想要这个,你可以这样做:

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...)>> ) {}
Run Code Online (Sandbox Code Playgroud)

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

现在

Foo( std::function<void(int)>( Dummy ) ); // OK, template argument deduction
Run Code Online (Sandbox Code Playgroud)

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

你可以通过一些愚蠢的行为来支持两者:

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...)> ) {}
Run Code Online (Sandbox Code Playgroud)

但这不支持在没有更多愚蠢行为的情况下传递部分参数。


归档时间:

查看次数:

169 次

最近记录:

4 年,3 月 前