我有一个函数模板,它取一个std::pair
以及一对类型的值。我想使用std::map
中的条目作为对参数来调用此函数。
#include <map>
#include <utility>
template <typename T1, typename T2>
void do_stuff(std::pair<T1, T2> const &pair, T1 const &val) {
// Imagine that this does something important...
}
int main() {
std::map<int, float> foo { { 0, 0.0 } };
do_stuff(*foo.begin(), 0);
}
这无法编译,因为地图条目的类型为std::pair<const int, float>
,因此T1
的类型扣除具有冲突类型:const int
通过pair
参数,以及int
通过val
参数。
test.cc: In function ‘int main()’:
test.cc:12:27: error: no matching function for call to ‘do_stuff(std::pair<const int, float>&, int)’
do_stuff(*foo.begin(), 0);
^
test.cc:5:6: note: candidate: template<class T1, class T2> void do_stuff(const std::pair<_T1, _T2>&, const T1&)
void do_stuff(std::pair<T1, T2> const &pair, T1 const &val) {
^~~~~~~~
test.cc:5:6: note: template argument deduction/substitution failed:
test.cc:12:27: note: deduced conflicting types for parameter ‘const T1’ (‘const int’ and ‘int’)
do_stuff(*foo.begin(), 0);
^
解决此冲突的最佳方法是什么?理想情况下,我希望将T1
推断为int
,但如果它更直接实现,则可以const int
。
我发现我可以在std::remove_const
参数的类型上使用std::decay
或val
来解决错误:
void do_stuff(std::pair<T1, T2> const &pair, typename std::remove_const<T1>::type const &val) {
但我不知道哪一个更合适,或者是否有其他解决方案更好。
答案 0 :(得分:5)
一种解决方案是使用std::add_const
而不是直接使用关键字const
。
通过模板进行的往返可以防止通过此参数类型进行类型扣除:
#include <map>
#include <type_traits>
#include <utility>
template< class T1, class T2 >
void do_stuff( std::pair<T1, T2> const& pair, std::add_const_t<T1>& val )
{
// Imagine that this does something important...
(void) pair; (void) val;
}
auto main()
-> int
{
std::map<int, float> foo { { 0, 0.0f } };
do_stuff(*foo.begin(), 0);
}
答案 1 :(得分:3)
template<class T>struct identity{using type=T;};
template<class T>using no_deduce=typename identity<T>::type;
将no_deduce
中的第二种类型换行以阻止扣除。
template <typename T1, typename T2>
void do_stuff(std::pair<T1, T2> const &pair, no_deduce<T1> const &val) {
// Imagine that this does something important...
}
这两种方法都有效,并且很清楚你为什么要这样做。
现在,如果T1
是引用类型,那么可能值得考虑您想要做什么,以及const&
在这种情况下做什么。想象一下,T1
为int&
,然后int& const&
变为int&
。
这可能不是你想要的。
也许你想要的,你真正想要的是:
template <typename T1, typename T2>
void do_stuff(std::pair<T1, T2> const &pair, std::remove_reference_t<T1> const &val) {
// Imagine that this does something important...
}
如果你想要一个const&
,你必须忘记&
,如果你想阻止val
被修改,你最好确保它是const
。现在,不要浪费你宝贵的const&
,remove_reference_t
,它会很好。
如果你想与volatile
打交道,你必须得到remove_volatile_t
。将它们永久地绑在一起template<class T>using clean_t=std::remove_cv_t<remove_reference_t<T>>
。如果你想const&
你必须
template <typename T1, typename T2>
void do_stuff(std::pair<T1, T2> const &pair, clean_t<T1> const &val) {
// Imagine that this does something important...
}
简单地const&
很简单,但就是这样。
答案 2 :(得分:2)
不是很优雅,但我想你可以在两个不同的模板参数中分开T1
。
像
这样的东西template <typename T1, typename T2, typename T3>
void do_stuff(std::pair<T1, T2> const &pair, T3 const &val) {
// Imagine that this does something important...
}
您可以通过std::enable_if_t
添加一项检查,以强制T1
和T3
之间的相关性;通过例子
template <typename T1, typename T2, typename T3,
typename = std::enable_if_t<std::is_same<std::remove_const_t<T1>, std::remove_const_t<T3>>::value>>
void do_stuff(std::pair<T1, T2> const &pair, T3 const & val) {
// Imagine that this does something important...
}