I have a function template that takes a std::pair
as well as a value of one of the pair's types. I'd like to call this function using an entry from a std::map
as the pair argument.
#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);
}
This fails to compile because the type of the map's entry is std::pair<const int, float>
, so the type deduction for T1
has conflicting types: const int
via the pair
argument, and int
via the val
argument.
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);
^
What's the best way to resolve this conflict? Ideally I'd like T1
to be deduced as int
, but it's OK for it to be const int
if that's more straightforward to implement.
I've found that I can resolve the error by using either std::remove_const
or std::decay
on the type of the val
parameter:
void do_stuff(std::pair<T1, T2> const &pair, typename std::remove_const<T1>::type const &val) {
but I don't know which of those is more appropriate, or if there's some other solution that'd be better.
const member functions may be invoked for const and non-const objects. non-const member functions can only be invoked for non-const objects. If a non-const member function is invoked on a const object, it is a compiler error.
Template argument deduction is used in declarations of functions, when deducing the meaning of the auto specifier in the function's return type, from the return statement.
Type inference or deduction refers to the automatic detection of the data type of an expression in a programming language. It is a feature present in some strongly statically typed languages. In C++, the auto keyword(added in C++ 11) is used for automatic type deduction.
One solution is to use std::add_const
instead of using the keyword const
directly.
The roundtrip via a template prevents type deduction via this parameter type:
#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);
}
template<class T>struct identity{using type=T;};
template<class T>using no_deduce=typename identity<T>::type;
Wrap the second type in no_deduce
to block deduction.
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...
}
This both works and is clear why you are doing it.
Now, it might be worth considering what you want to do if T1
is a reference type, and what const&
does in that case. Imagine T1
is int&
then int& const&
becomes just an int&
.
This may not be what you want.
Maybe what you want, what you really really want, is:
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...
}
If you want a const&
you gotta forget your &
, if you wanna prevent val
from being modified you better make sure it is const
. Now, don't go wasting your precious const&
, remove_reference_t
and it'll be just fine.
If you wanna deal with volatile
, you gotta get with remove_volatile_t
. Tie them together forever in a template<class T>using clean_t=std::remove_cv_t<remove_reference_t<T>>
. If you wanna const&
you have got to
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...
}
Simply const&
is to easy, but that's the way it is.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With