Reputation: 33
I'm writing a wrapper template class which can wrap an arbitrary type and imbue it with some additional semantics, but I can't figure out how to get overload resolution to work properly. The issue arises when a conversion that would ordinarily be resolved by comparing ranks of competing conversion sequences, cannot be deduced by the compiler because the type in question is a template argument, rather than a function argument. For instance,
#include <type_traits>
template <typename T> class Wrapper {
T val;
public:
Wrapper() = default;
template <typename U> Wrapper(Wrapper<U> x) : val(x.val) {}
};
void foo(Wrapper<const char *>) {}
void foo(Wrapper<bool>) {}
int main() {
Wrapper<char *> cp;
foo(cp);
}
Here, the call to foo() is ambiguous. The desired behavior would be for the compiler to select void foo(Wrapper<const char *>)
, as it would if cp
were instead a char *
and foo
were instead void foo(const char *)
. Is this possible?
EDIT: Thanks to everyone for the quick responses, but perhaps I should have been more clear. What I have given above is just an example. What I require is a general solution to the following question: given arbitrary types T
, U
, and V
, suppose that C++'s built in overload resolution would prefer the conversion T
-> U
over T
-> V
. How can I then also ensure that C++ would prefer Wrapper<T>
-> Wrapper<U>
over Wrapper<T>
-> Wrapper<V>
?
I made this clarification because it seemed that the answers were specifically addressing certain aspects of overload resolution, like cv-qualifiedness, whereas I really need a general solution.
Upvotes: 2
Views: 731
Reputation: 3849
The problem here is that both overloads have the exact same weight in the resolution because of the template.
If you want overload resolution to happen, you have to introduce overload resolution.
This can be done by adding the corresponding type as second (unused) parameter:
void foo(Wrapper<const char *>, const char *)
void foo(Wrapper<bool>, bool)
With the help of the following alias in your wrapper:
using value_type = T;
The following foo() function can select the best overload:
template <typename W>
void foo(W && w) {
foo(std::forward<W>(w), typename std::remove_reference_t<W>::value_type{});
}
Upvotes: 1
Reputation: 1
There are few things you can do:
#include <type_traits>
template <typename T> class Wrapper {
T val;
public:
T getVal() const {
return val;
}
Wrapper() = default;
template <typename U,
class = typename std::enable_if<std::is_same<typename std::remove_cv<typename std::remove_pointer<T>::type>::type,
typename std::remove_pointer<U>::type>::value>::type>
Wrapper(Wrapper<U> x) : val(x.getVal()) {}
};
void foo(Wrapper<const char *>) {}
void foo(Wrapper<bool>) {}
int main() {
Wrapper<char *> cp;
foo(cp);
}
Using this you can allow only a certain set of conversions, i.e. : X *-> const X *, conversions between integer types, etc.
UPDATE: Unfortunately, it seems that you cannot imitate the standard overload resolution rules, because all you can use is the conversion operator, and in terms of overload resolution it has the constant rank
Upvotes: 0
Reputation: 19607
You need to make the constructor less greedy. This can be done via SFINAE:
template <typename T>
using remove_const_from_pointer_t =
std::conditional_t<std::is_pointer<T>::value,
std::add_pointer_t<std::remove_const_t<std::remove_pointer_t<T>>>, T>;
template <typename T>
class Wrapper {
T val;
template <typename U>
friend class Wrapper;
public:
Wrapper() = default;
template <
typename U,
std::enable_if_t<
std::is_same<U, remove_const_from_pointer_t<T>>::value, int*> = nullptr>
Wrapper(Wrapper<U> x) : val(x.val) {}
};
You might want to try this instead of my remove_const_from_pointer_t
.
Also notice that I had to add a friend
declaration.
Edit: this does not work in case of just one void foo(Wrapper<bool>)
overload, you'd have to move the application of SFINAE from the Wrapper
's constructor directly to this overload:
template <
typename T,
std::enable_if_t<
std::is_same<std::remove_const_t<T>, char>::value, int*> = nullptr>
void foo(Wrapper<T *>) { }
Upvotes: 1
Reputation: 12751
You are missing const
in front of char*
in the main.
Declare as said below. It should work.
Wrapper<const char *> cp;
Below is the test and the results
http://rextester.com/FNOEL65280
Upvotes: 0