What makes an overloaded function prefered to another? - c++

I have implemented a simple BooleanVariable class, where I get the boolean value from template parameter V, and it's comparison function operator==:
#include <iostream>
template<bool V>
struct BooleanVariable {
BooleanVariable() = default;
constexpr bool operator()() const {
return V;
}
};
template<bool V>
bool operator==(const BooleanVariable<V> &, const BooleanVariable<V> &) {
return true;
}
template<bool V1, bool V2>
bool operator==(const BooleanVariable<V1> &, const BooleanVariable<V2> &) {
return false;
}
int main() {
std::cout<<(BooleanVariable<true>() == BooleanVariable<false>())<<"\n";
std::cout<<(BooleanVariable<false>() == BooleanVariable<true>())<<"\n";
std::cout<<(BooleanVariable<true>() == BooleanVariable<true>())<<"\n";
std::cout<<(BooleanVariable<false>() == BooleanVariable<false>())<<"\n";
return 0;
}
operator== works perfectly, that I wonder why these 2 comparison functions don't have ambiguity problem for the case when V1 == V2, as both of fit to the template. Why is
template<bool V1, bool V2>
bool operator==(const BooleanVariable<V1> &, const BooleanVariable<V2> &) {
return false;
}
not triggered when V1 == V2? Can someone explains to me?

A good rule of thumb for overload resolutions where templates are involved is that more specialized candidates are preferred over more generic candidates. For example:
void foo(int);
template <class T>
void foo(T);
If you call foo like foo(42), the non-templated version will be called since it is more specialized. If you call it with any other type, the templated version will be called.
Another example:
template <class T>
void foo(T, T);
template <class T, class Y>
void foo(T, Y);
Here, the second template is obviously more general. So, for cases where types of both arguments are the same, the first template will be preferred. For other cases, the second template will be preferred.
The same applies to your example. The first operator can only handle arguments of the same type while the second one can handle arguments of differing types as well as identical types. Thus, the first one will be called if possible. Otherwise the second one will be called.

In the case of the operator ==, both arguments are of (template) type BooleanVariable, no ambiguity. The two variants of operator && have one argument of a template type, and one of BooleanVariable. The first one has a BooleanVariable as the first arg, and the second variant has a BooleanConstant as the second type. That makes both definitions perfectly viable for being chosen and that's the ambiguity the compiler warns you about when you write BooleanVariable() && BooleanVariable(). If you look carefully both variants perfectly match the expression and does not something do differentiate them, This is not exactly the case of operator ==, though - here when both arguments of type BooleanVariable have their template non-type parameter with the same value the first variant is chosen, otherwise when they are different the second one (with two bool template non-type parameters) is chosen.

Related

How do I add a template specialization when for a generic method on a generic class when the two types are equal?

I'm trying to add in a specialization where the generic type of method and class agree, but I haven't been able to figure out exactly how to specify the template instantiation (if it is even possible).
My best guess would be something like the following (though it obviously doesn't compile):
template<typename ClassT>
class Foo
{
public:
ClassT x;
template<typename MethodT>
void Bar(MethodT arg)
{
}
};
template<typename T>
template<>
void Foo<T>::Bar(T arg)
{
x = arg;
}
As is usually the case when considering function template specialization, an overload can handle it:
template<typename MethodT>
void Bar(MethodT arg)
{
}
void Bar(ClassT arg)
{
x = arg;
}
When you call Bar, one of the candidates will be a function template specialization and one won't. Think of the class template as stamping out real, concrete member functions where possible when it's instantiated. There's a rule pretty late in overload resolution to prefer the one that isn't a function template specialization if it's a tie up to that point.
What you end up with is the second overload being called when there's an "exact match" in types (which allows for a difference in top-level const). If exact matches are too narrow, you can restrict the first overload to widen the second:
// Allow the other overload to win in cases like Foo<int>{}.Bar(0.0).
// std::enable_if works as well before C++20.
template<typename MethodT>
void Bar(MethodT arg) requires (not std::convertible_to<MethodT, ClassT>)
{
}
As discussed in the comments, it's not possible to do this with template specialization. However, something similar can be accomplished by using std::enable_if_t and
template<typename ClassT>
class Foo
{
public:
ClassT x;
template<typename MethodT,
typename = std::enable_if_t<!std::is_same<ClassT, MethodT>::value>>
void Bar(MethodT arg)
{
}
void Bar(ClassT arg)
{
x = arg;
}
};
std::enable_if_t will only return a valid type when the input type arg is true. Therefore, the template substitution will fail when MethodT and ClassT are the same type, but the non-template overload will not fail. The template substitution failure is ok under SFINAE.

Template func with cond. const argument + template arg deduction

I am implementing a wrapper class that wraps some base class object. I want the wrapper to be as unnoticeable as possible and therefore, I have already overloaded the -> operator to return a reference to the wrapped object in order to provide immediate access to the base-class's interface.
Additionally, I want my wrapper type to also implement all operators that the base-class implements and just delegates the respective operator calls to the wrapped object. However, since I want to be able to wrap any arbitrary base-class, I don't know a priori what operators are defined for the wrapped class.
My current approach consists of just defining all operators for my wrapper and using SFINAE to disable those implementations that are not covered by base-class implementations.
The implementation for the operator overload essentially always looks like this
auto operator <operator> (const Wrapper &lhs, const Wrapper &rhs) { return lhs.get() <operator> rhs.get(); }
auto operator <operator> (const Wrapper &lhs, const Base &rhs) { return lhs.get() <operator> rhs; }
auto operator <operator> (const Base &lhs, const Wrapper &rhs) { return lhs <operator> rhs.get(); }
where <operator> is the respective operator. Since, I don't want to duplicate this code for all operators, I have defined some macros to create the definitions for me.
This works for most operators, but now I also want to support the various assignment operators (e.g. *=). Here, the lhs argument must not be const as it is modified via the action of the operator.
I could just generate these separately, but I thought that there must be a way to simply make the lhs argument const or non-const depending on a constexpr boolean (available as macro-parameter). Thus, I created a templated helper cond_add_const< T, bool > that returns const T if passed true and T if passed false.
The problem now is, that the overloads that involve the Base class as direct parameters fail to be resolved due to template argument deduction failure. The problem seems to be that in order to apply my conditional const, I essentially have the replace the respective type with cond_add_const< T, true >::type and apparently everything to the left of :: does not participate in template argument deduction.
This seems rather frustrating and it also feels like for my simple case there should be a possibility to circumvent this limitation (or choose an approach that does not require it), but I just can't come up with one (that does not involve duplicating code). Any ideas?
MWE illustrating my problem:
#include <type_traits>
template< typename T, bool is_const > struct cond_add_const {
using type = typename std::conditional< is_const, typename std::add_const< T >::type, typename std::remove_const< T >::type >::type;
};
template< typename T > void myFunc(typename cond_add_const< T, true >::type lhs, int rhs) {
}
int main() {
myFunc(1, 2);
}
Compiling this snippet with g++ results in
test.cpp: In function ‘int main()’:
test.cpp:11:13: error: no matching function for call to ‘myFunc(int, int)’
11 | myFunc(1, 2);
| ^
test.cpp:7:29: note: candidate: ‘template<class T> void myFunc(typename cond_add_const<T, true>::type, int)’
7 | template< typename T > void myFunc(typename cond_add_const< T, true >::type lhs, int rhs) {
| ^~~~~~
test.cpp:7:29: note: template argument deduction/substitution failed:
test.cpp:11:13: note: couldn’t deduce template parameter ‘T’
11 | myFunc(1, 2);
|
You might turn your template functions in non-template friend function of your class:
template <typename T>
struct Wrapper
{
// ...
friend void myFunc(typename cond_add_const<Wrapper<T>, true >::type lhs, T rhs)
{ /*...*/ }
};
Demo
There are some caveats:
the friend function can only be found via "ADL" (So at least one parameter should be of the Wrapper type).

Why is template template function overload chosen over the one with the concept?

The following code fails in static_assert:
#include <iostream>
#include <concepts>
#include <vector>
template <typename T>
concept container_type = requires(T& t) {
typename T::value_type;
typename T::reference;
typename T::iterator;
{ t.begin() } -> std::same_as<typename T::iterator>;
{ t.end() } -> std::same_as<typename T::iterator>;
};
// overload (0), fallback case
template <typename T>
constexpr int foo(const T& o) {
return 0;
}
// overload (1), using the concept
constexpr int foo(const container_type auto& o) {
return 1;
}
// overload (2), using template template parametar
template <typename T, template <typename ...> typename C >
constexpr int foo(const C<T>& o) {
return 2;
}
int main()
{
std::vector<int> a {1,3,3,44,55,5,66,76};
static_assert(foo(a) == 1);
return 0;
}
Compiler used is GCC on https://coliru.stacked-crooked.com, command line is g++ -std=c++20 -O2 -Wall -pedantic -pthread.
I would expect that the overload (1) should be used as the best match, but overload (2) is used instead.
Is this standard conformant behavior?
Constraints are considered for the purpose of choosing between two viable overloads only if there is no other tie breaker between the overloads, considered as unconstrained. In particular that means that if one is more specialized by the old partial ordering rules for function templates, ignoring constraints, then that will be chosen and constraints are not relevant.
Basically the constraints only matter when comparing function templates that are, except for the constraints, exactly equivalent in terms of their function parameters.
Leaving out the constraint (which is satisfied with the foo(a) call in main) your first overload looks like
constexpr int foo(const auto& o) {
return 1;
}
Now if you compare this to the second overload I think it should be clear that by the usual partial ordering rules for function templates this is less specialized than the second overload. As a consequence the second overload is chosen.
Even if you recast the conditions under which the second overload is valid into a concept Overload2Concept and then use
constexpr int foo(const Overload2Concept auto& o) {
return 2;
}
instead, it won't work, because the constraints of the two overloads are not subsets of one another. You can have a container_type per your definition which is not a specialization of a template and you can have a specialization of a template template <typename ...> class C; which is not a container_type.
So in that case neither would be more specialized than the other, neither by old partial ordering rules nor by new constraint ordering rules, and consequently overload resolution would be ambiguous.
Having concepts/constraints integrated into overload resolution in this way guarantees that adding additional overloads with constraints to pre-C++20 code won't cause any break in the way overloads were chosen beforehand. Were constraints allowed to overrule the old partial ordering in the way you seem to expect, then it would be dangerous to modify any pre-C++20 overload set of functions with constraints.
The following overload would be preferred over overload (2) because it is more specialized per constraints and the unconstrained equivalents identical:
template <typename T, template <typename ...> typename C >
requires container_type<C<T>>
constexpr int foo(const C<T>& o) {
return 1;
}

The meaning of more specialized template in overload resolution rules

Recently I posted this question : Ambigous operator overload on clang which got closed for being essentially the same as Is clang wrongfully reporting ambiguity when mixing member and non-member binary operators?
After reading answers to that other question, I think I am still confused about what more specialized means. So based on what I read there, the member operator from my code should resolve to this :
template <class U>
TVector3<T>& TVector3<float>::operator*=(const U ) { return *this; }
during name lookup and for the purpose of overload resolution I would get these two candidates:
template <class U>
TVector3<T>& operator*=(TVector3<float>&, const U ) { return *this; }
template <typename T>
TVector3<T>& operator*=(TVector3<T>& v, const TMatrix3<T>& ) { return v; }
Why are they both equally well specialized ?
Is it because in the first overload the first argument is more specialized, while in the second overload the second argument is more specialized ? So basically a tie between them ?
On the other hand, if I turned the member operator*= of the Vector class into a free function, the overload resolution set would be:
template <class T, class U>
TVector3<T>& operator*=(TVector3<T>& v, const U ) { return v; }
template <typename T>
TVector3<T>& operator*=(TVector3<T>& v, const TMatrix3<T>& ) { return v; }
in which case the first argument of both overloads is equally specialized, but the second argument of the second overload is more specialized and thus no ambiguity in this case.
Is my understanding correct ?

Function overloads and inherited types are not calling the correct overload

Given the following base class:
struct ValueType {
String
ToString(String const& format) const;
};
I want this overload to be called for types deriving from ValueType:
String FormatValue(const ValueType& value, const String& format)
{
return value.ToString(format);
}
Otherwise, I want this overload to be called:
template <typename T>
String FormatValue(const T& value, const String& format);
How can I ensure that derived types do not instead call the second overload?
The original question is located here.
I am not too fond of what you are trying to do for different reasons (including the ValueType interface, why not use AnyToString always?), but at any rate you should be able to solve your problem with SFINAE
template <typename T>
typename enable_if< !is_base_of<ValueType, T>::value, String>::type
FormatValue( T const & value, const String& format ) { ... }
What that code does (once you make it compile :) is inhibiting the template function when the condition is met. When the compiler considers the template as an overload it will try to substitute the type, and the enable_if instantiation will fail to have a nested type if the condition is met, so the substitution fails and the template is discarded. After that, the best overload will be the version that takes a ValueType object.