Reputation: 1101
Is there a way to ensure the template overload isn't selected unless all other overloads fail, without resorting to enable_if?
Int should be handled by the long overload, but it's being handled by the template overload, which the compiler doesn't like.
class SetProxy {
public:
void operator=(const TemplateString& value) {
dict_.SetValue(variable_, value);
}
template<class T>
void operator=(const T& value) {
dict_.SetValue(variable_, TemplateString(value.data(), value.size()));
}
void operator=(long value) {
dict_.SetIntValue(variable_, value);
}
}
Upvotes: 0
Views: 90
Reputation: 153830
The problem with the code in the question is that the template is the best match if an argument convertible to long
or TemplateString
is passed: the template doesn't need to do a conversion while calling the other functions would involve a conversion.
The desired behavior can be achieved even without the use of std::enable_if
although I think prohibiting the use of std::enable_if
is a made-up requirements: even if you can't use C++2011 or Boost, it is extremely simple to implement std::enable_if
. Implementing some of the desired type traits is a bit harder but doable. Restricting its use effectively means that you'll need to implement essentially the same logic in a more or less contrived way which doesn't say what it really does. For example, this code doesn't use std::enable_if
or SFINAE but there is an extra object actually be created which wouldn't be needed if SFINAE had been used:
#include <iostream>
#include <string>
class SetProxy {
template <typename F>
struct helper {
helper(F const& v): value_(v) {}
F const& value_;
};
template<typename F>
void aux(helper<F> value, ...) {
std::cout << "template " << value.value_ << "\n";
}
template<typename F>
void aux(long value, int) {
std::cout << "long: " << value << "\n";
}
template<typename F>
void aux(std::string const& value, int) {
std::cout << "string: " << value << "\n";
}
public:
template<typename T>
void operator=(const T& value) {
this->aux<T>(value, 0);
}
};
struct foo {};
std::ostream& operator<< (std::ostream& out, foo const&) {
return out << "foo";
}
int main()
{
SetProxy p;
p = 17l;
p = 17;
p = foo();
p = "hello";
p = std::string("hello");
}
It doesn't use the types from the original question because I don't have them accessible and I didn't feel like typing things irrelevant to the actual question. Note that this effectively contains the important bits of an implementation of std::is_convertible
. Internally, it is necessary to forward to another template because the assignment operator cannot have a variable argument list: since there are viable conversions from int
to helper<int>
, there would be an ambiguity if there weren't anything else to distinguish the types, a variable argument list is used to make the template version a bit worse.
Just for reference, here is the in my opinion more readable version using std::enable_if
:
class SetProxy {
public:
template <typename T>
typename std::enable_if<!std::is_convertible<T, long>::value
&& !std::is_convertible<T, std::string>::value, void>::type
operator= (T const& value) {
std::cout << "template: " << value << "\n";
}
void operator= (long value) {
std::cout << "long: " << value << "\n";
}
void operator= (std::string value) {
std::cout << "std::string: '" << value << "'\n";
}
};
Upvotes: 0
Reputation: 601
It's not unexpected that int matches the template version here. Template argument matching will get a positive match for const int&
and pass that to overload resolution. const int&
is a better match for int
than long
is. If you want to avoid calling the template version for int types, then I suggest you add an explicit overload for int.
Upvotes: 0
Reputation: 96241
Why would int
be handled by the long
overload? They aren't the same type, and the template with T = int
is a perfect match for int
while long
is not a perfect match.
Can you give us more information about the underlying problem you're trying to solve?
Upvotes: 5