Reputation: 3991
Assume the following situation:
Type A
and type B
, B
can be implicitly converted to A
but the opposite is untrue.
I have a function
template<class T>
void do_stuff(T a, T b);
I want to call said function as such:
do_stuff(A{}, B{});
The problem here is that the compiler can't deduce the type and instead says:
template argument deduction/substitution failed
I can call my function like this:
do_stuff<A>(A{}, B{});
But this is more annoying for the user.
Alternatively I can do something like this:
template<class T, class M>
void do_stuff(T a, M b);
But then b goes on its merry way to be of type B (with the prior invocation).
Ideally I would like something like:
template<class T, class M = T>
void do_stuff(T a, M b);
Or:
template<class T@INSERT MAGIC SO THAT T IS DEDUCED AS BEING THE TYPE OF ARGUMENT NR 1@>
void do_stuff(T a, T b);
Is such a thing possible ?
Upvotes: 15
Views: 1964
Reputation: 69864
another way, seeking to express intent declaratively:
#include <type_traits>
// a B
struct B{};
// an A can be constructed from a B
struct A{
A() {};
A(B) {};
};
// prove that A is constructible from B
static_assert(std::is_convertible<B, A>::value, "");
// enable this function only if a U is convertible to a T
template<
// introduce the actors
class T, class U,
// declare intent
std::enable_if_t<std::is_convertible<U, T>::value>* = nullptr
>
void do_stuff(T, U)
{
}
int main()
{
// legal
do_stuff(A{}, B{});
// does not compile
// do_stuff(B{}, A{});
}
update:
to force the conversion, a lambda can be used:
// enable this function only if a U is convertible to a T
template<class T, class U,
std::enable_if_t<std::is_convertible<U, T>::value>* = nullptr
>
void do_stuff(T a, U b)
{
return[](T& a, T b) -> decltype(auto)
{
}(a, b);
}
Upvotes: 1
Reputation: 2115
There is answer in C++11: std::common_type
http://en.cppreference.com/w/cpp/types/common_type
template<typename A>
void f_impl(A a, A b)
{
}
template<typename A, typename B>
void f(A a, B b)
{
f_impl<typename std::common_type<A, B>::type>(a, b);
}
struct Z
{
};
struct W
{
operator Z();
};
int main()
{
f(1u, 1l); //work
f(W{}, Z{});
f(Z{}, W{}); //and this work too
}
Upvotes: 8
Reputation: 302757
Wrap b
in a non-deduced context. That way, only a
will be deduced and b
must be converted to that type.
template <class T> struct dont_deduce { using type = T; };
template <class T> using dont_deduce_t = typename dont_deduce<T>::type;
template<class T>
void do_stuff(T a, dont_deduce_t<T> b);
Upvotes: 21
Reputation: 70267
It's certainly possible, just with a little delegation. You've made the problem pretty easy by specifying that you always want the inferred type to be the type of the first argument, so all we need to do is drop a little hint to the compiler.
template <class T>
void do_stuff_impl(T a, T b) {
cout << "Doing some work..." << endl;
}
template <class T, class S>
void do_stuff(T a, S b) {
do_stuff_impl<T>(a, b);
}
Now the user can call do_stuff
with any two arguments, and C++ will try to implicitly cast the second argument to match the type of the first. If the cast isn't valid, you'll get a template instantiation error. On GCC, it says cannot convert ‘b’ (type ‘A’) to type ‘B’
, which is pretty accurate and to the point. And any compiler worth its salt is going to be able to inline that delegated call, so there should be negligible overhead.
Upvotes: 7