为什么参数包不扩展为正确的类型?
Why doesn't parameter pack expand to correct type?
我有一段有点做作的代码:
#include <functional>
template <typename... ARGs>
auto construct1(std::function<void(ARGs...)> p, const ARGs &...args) {}
template < typename... ARGs>
auto construct2(std::function<void(int)> p, const ARGs &...args) {}
int main() {
auto p = [](int) {};
construct1<int>(p, 0);
construct2<int>(p, 0);
return 0;
}
为什么编译器在第一种情况下努力解决 ARGs... = { int }
?如果我通过在签名中使用 std::function<void(int)>
来协助编译器(第二种情况),代码可以正常编译。在这两种情况下,编译器都可以毫不费力地推断出 const ARGs&...
应该是 const int&
。使用 C++17.
海湾合作委员会:
main.cpp: In function ‘int main()’:
main.cpp:11:25: error: no matching function for call to ‘construct1<int>(main()::<lambda(int)>&, int)’
11 | construct1<int>(p, 0);
| ^
main.cpp:4:6: note: candidate: ‘template<class ... ARGs> auto construct1(std::function<void(ARGs ...)>, const ARGs& ...)’
4 | auto construct1(std::function<void(ARGs...)> p, const ARGs &...args) {}
| ^~~~~~~~~~
main.cpp:4:6: note: template argument deduction/substitution failed:
main.cpp:11:25: note: ‘main()::<lambda(int)>’ is not derived from ‘std::function<void(ARGs ...)>’
11 | construct1<int>(p, 0);
|
叮当声:
main.cpp:11:5: error: no matching function for call to 'construct1'
construct1<int>(p, 0);
^~~~~~~~~~~~~~~
main.cpp:4:6: note: candidate template ignored: could not match 'function<void (int, type-parameter-0-0...)>' against '(lambda at main.cpp:10:14)'
auto construct1(std::function<void(ARGs...)> p, const ARGs &...args) {}
^
1 error generated.
问题是,construct1
正在使用 std::function
,但您正在传递 lambda。当您将参数设置为 std::function<void(ARGs...)>
、template argument deduction is performed to deduce ARGs
on the function parameter p
(even template parameter pack is explicitly specified with template arguments 类型时,如果有其他参数,它可能会通过模板参数推导进行扩展),这会失败,因为在推导中不会考虑隐式转换。
Type deduction does not consider implicit conversions (other than type adjustments listed above): that's the job for overload resolution, which happens later.
您可以使用std::type_identity
(since C++20) to exclude p
from deduction (see non-deduced contexts),例如
template <typename... ARGs>
auto construct1(std::function<void(std::type_identity_t<ARGs>...)> p, const ARGs &...args) {}
// ^^^^^^^^^^^^^^^^^^^^^ ^
PS:在 C++20 之前,您可以轻松地赢得 type_identity
为:
template< class T >
struct type_identity {
using type = T;
};
template< class T >
using type_identity_t = typename type_identity<T>::type;
我有一段有点做作的代码:
#include <functional>
template <typename... ARGs>
auto construct1(std::function<void(ARGs...)> p, const ARGs &...args) {}
template < typename... ARGs>
auto construct2(std::function<void(int)> p, const ARGs &...args) {}
int main() {
auto p = [](int) {};
construct1<int>(p, 0);
construct2<int>(p, 0);
return 0;
}
为什么编译器在第一种情况下努力解决 ARGs... = { int }
?如果我通过在签名中使用 std::function<void(int)>
来协助编译器(第二种情况),代码可以正常编译。在这两种情况下,编译器都可以毫不费力地推断出 const ARGs&...
应该是 const int&
。使用 C++17.
海湾合作委员会:
main.cpp: In function ‘int main()’:
main.cpp:11:25: error: no matching function for call to ‘construct1<int>(main()::<lambda(int)>&, int)’
11 | construct1<int>(p, 0);
| ^
main.cpp:4:6: note: candidate: ‘template<class ... ARGs> auto construct1(std::function<void(ARGs ...)>, const ARGs& ...)’
4 | auto construct1(std::function<void(ARGs...)> p, const ARGs &...args) {}
| ^~~~~~~~~~
main.cpp:4:6: note: template argument deduction/substitution failed:
main.cpp:11:25: note: ‘main()::<lambda(int)>’ is not derived from ‘std::function<void(ARGs ...)>’
11 | construct1<int>(p, 0);
|
叮当声:
main.cpp:11:5: error: no matching function for call to 'construct1'
construct1<int>(p, 0);
^~~~~~~~~~~~~~~
main.cpp:4:6: note: candidate template ignored: could not match 'function<void (int, type-parameter-0-0...)>' against '(lambda at main.cpp:10:14)'
auto construct1(std::function<void(ARGs...)> p, const ARGs &...args) {}
^
1 error generated.
问题是,construct1
正在使用 std::function
,但您正在传递 lambda。当您将参数设置为 std::function<void(ARGs...)>
、template argument deduction is performed to deduce ARGs
on the function parameter p
(even template parameter pack is explicitly specified with template arguments 类型时,如果有其他参数,它可能会通过模板参数推导进行扩展),这会失败,因为在推导中不会考虑隐式转换。
Type deduction does not consider implicit conversions (other than type adjustments listed above): that's the job for overload resolution, which happens later.
您可以使用std::type_identity
(since C++20) to exclude p
from deduction (see non-deduced contexts),例如
template <typename... ARGs>
auto construct1(std::function<void(std::type_identity_t<ARGs>...)> p, const ARGs &...args) {}
// ^^^^^^^^^^^^^^^^^^^^^ ^
PS:在 C++20 之前,您可以轻松地赢得 type_identity
为:
template< class T >
struct type_identity {
using type = T;
};
template< class T >
using type_identity_t = typename type_identity<T>::type;