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 ?
Related
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;
}
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.
Given a template class A with a single template argument T, is it possible to only overload operators in A which are available for type T? For example:
template <typename T>
class A
{
public:
#if hasOperator(T, +=)
T& operator +=(const T &rhs)
{
mValue += rhs;
return mValue;
}
#endif
private:
T mValue;
}
int main()
{
A<int> a;
a += 8; //+= will forward to the += for the int
struct Test { /*no operators defined*/ };
A<Test> b; //+= is not implemented since Test does not implement +=
}
I'm writting a generic wrapper class that needs to behave exactly like the template type. So if T has operator +=, A will (at compile time) overload += accordingly. Yes, I could go ahead and just implement every operator in A, but then the compiler will error when T doesn't have a certain operator. At first I though template specialization might be the answer, but that would require a specialization for every type. While this could work and be a lot of typing, it wont because A needs to work with any type (not just what is specialized).
Use expression SFINAE to drop your operator+ from the overload resolution set unless T defines operator+
template <typename T>
class A
{
private:
T mValue;
public:
template<typename U=T>
auto operator +=(const U &rhs)
-> decltype(mValue += rhs)
{
mValue += rhs;
return mValue;
}
};
Live demo
I will give 3 solutions in decreasing complexity and utility. The last solution is the simplest, and least complex.
A small, if useful, metaprogramming library:
template<class...>struct types{using type=types;};
namespace details {
template<template<class...>class Z, class types, class=void>
struct can_apply : std::false_type {};
template<template<class...>class Z, class...Ts>
struct can_apply<Z,types<Ts...>,std::void_t<Z<Ts...>>> :
std::true_type
{};
}
template<template<class...>class Z, class...Ts>
using can_apply = details::can_apply<Z,types<Ts...>>;
A trait for the result of +=:
template<class Lhs, class Rhs>
using plus_equal_result = decltype(std::declval<Lhs>()+=std::declval<Rhs>());
template<class Lhs, class Rhs>
using can_plus_equal = can_apply< plus_equal_result, Lhs, Rhs >;
template<class T>
using can_self_plus_equal = can_plus_equal< T&, T const& >;
which gives us some nice traits that return true type or false type depending on if += is valid.
template<class A, class T, bool b = can_self_plus_equal<T>{}>
struct A_maybe_plus_equal {};
template<class A, class T>
struct A_maybe_plus_equal<A, T, true> {
A& self() { return *static_cast<A*>(this); }
A& operator+=( T && t )
{
self().mResult += std::move(t);
return self();
}
template<class U>
std::enable_if_t<can_plus_equal<T&,U>{},A&> operator+=( U && u )
{
self().mResult += std::forward<U>(u);
return self();
}
};
which gives us a += iff we pass true.
template <class T>
class A:
public A_maybe_plus_equal<A<T>, T>
{
friend class A_maybe_plus_equal<A<T>, T>;
public:
// nothing needed
private:
T mValue;
};
which gives you a += overload that takes a const T& or a T&& or a U&& on the right hand side if and only if T& += T const& is a valid expression.
This is the "perfect" solution, but it is complex.
Note that each operator can be done separately, so you don't have a combinatorial explosion of specializations.
Now, the there is an easier option. It has the downside that it doesn't support {} based construction on the right hand side, and under some readings of the standard it is illegal.
It is, however, still SFINAE friendly:
template <typename T>
class A {
public:
template<class U>
auto operator +=(U&&rhs)
-> decltype( (std::declval<T&>()+=std::declval<U&&>()),void(),A& )
// or std::enable_if_t<can_plus_equal<T&,U>{},A&>
{
mValue += std::forward<U>(rhs);
return *this;
}
private:
T mValue;
};
This can be folded into the above option, and give both {} and perfect forwarding syntax. I find that the T const& can be dropped if you have a template perfect forwarder.
The reason why this is technically undefined behavior is that the standard mandates that all template functions have at least one set of arguments that would render their body able to compile. In a given class instance, the template += of the above may have no such set of type arguments, which makes your program ill-formed with no diagnostic required (ie, UB).
There is another rule that member functions of template classes don't get instantiated unless called. Some argue that this other rule supersedes the one I mentioned in the last paragraph.
Another argument is that the method may be legal so long as there is some mixture of template arguments to the enclosing class(es) and to the template method itself that lead to it being instantiatable. I'd guess that this is what the standard committee intended, but I don't know how to read the standard to get this result.
This argument also applies to the plus_equal function in answer #1. That implementation need not be as simple. In addition, #1 provides {} based += syntax, which is a practical reason to use it. This concern -- that the program is technically ill-formed -- is academic, as all compilers I have used have no problems with this construct.
The paragraph three above this one gives us our final option. Do nothing.
template <typename T>
class A {
public:
A& operator +=(const T &rhs) {
mValue += rhs;
return *this;
}
private:
T mValue;
};
which means that you cannot SFINAE test that += doesn't work, but so long as you don't call += it "works". This is how vector's operator< works, for example. This is a lower "quality" solution, and cases of this in the standard library tend to be repaired over time.
However, as a first pass, this last choice is usually best. Only if you expect SFINAE requirements are the above hoops worthwhile.
Ultimately, C++1z is introducing concepts. I believe concepts will make this problem much easier, as eliminating overloads from consideration based on the type arguments of the enclosing class is a perennial problem in std.
You don't actually have to do anything. Individual member functions of a template class won't get instantiatiated until use. You say:
but then the compiler will error when T doesn't have a certain operator.
But isn't that clearer than having it error when A<T> doesn't? If you have:
template <typename T>
class A
{
public:
A& operator +=(const T &rhs)
{
mValue += rhs;
return *this;
}
A& operator-=(const T &rhs)
{
mValue -= rhs;
return *this;
}
// etc
private:
T mValue;
};
Then this will just work:
int main() {
A<int> a;
a += 8; //+= will forward to the += for the int
struct Test {
Test& operator-=(const Test& ) { return *this; }
};
A<Test> b;
b -= Test{}; // totally fine
b += Test{}; // error: no match for +=
// (operand types are 'main()::Test' and 'const main()::Test')
}
Recently I came over an issue with clang++ 5.0.0 compiler where via ADL it was not picking up the correct function on Mac (but g++ did it correctly on Linux). I would like to know whether its a compiler issue OR poor class design in general.
Here is an example code (purely for illustration purpose):
namespace test {
class Ops {
public:
Ops():val_(0){}
template<typename T>
Ops& operator<< (const T& val) {
std::cout << "Called member function" << std::endl;
this->val_ = val;
return *this;
}
private:
int val_;
};
template<typename T>
struct Any {
T val_;
};
template <template<typename> class E, typename T>
Ops& operator<< (Ops& op, const E<T>& val) {
std::cout << "Global function" << std::endl;
return op;
}
}
int main() {
test::Ops op;
int k = 9;
test::Any<int> a;
op << a;
return 0;
}
I would like to know how ADL and template argument deduction wouldwork in step wise manner to find the best match ?
Would there be any situation for the same 'main body' the member function would be preferred in place of the free function ? (This is what is happening in the product build)
Thanks in advance.
This is what happens in detail and what every compiler should do: a candidate template function is found by qualified lookup
template <typename T>
test::Ops::operator<<(const T&)
while the second candidate is generated via ADL using template argument deduction (cfr. temp.deduct.conv)
template <template <typename> class E, typename T>
test::operator<<(test::Ops&, const E<T>&)
Afterwards overload resolution kicks in (cfr. 13.3.3) and the non-member one (F1) is preferred to the member (F2) one since
F1 and F2 are function template specializations, and the function template for F1 is more specialized
than the template for F2 according to the partial ordering rules described in 14.5.6.2.
and thus selected as the function to be called.
To answer your question: it depends on the overload resolution rules. Being a member function or in an inner scope doesn't affect the result and something like
namespace test {
class Ops {
public:
Ops():val_(0){}
template<typename T>
Ops& operator<< (const T& val) {
std::cout << "Called member function" << std::endl;
this->val_ = val;
return *this;
}
private:
int val_;
};
template<typename T>
struct Any {
T val_;
};
template <typename E>
Ops& operator<< (Ops& op, const E& val) {
std::cout << "Global function" << std::endl;
return op;
}
}
would just trigger an overload resolution error 'use of overloaded operator '<<' is ambiguous'.
As a plus: the member function is wrong even if it were chosen: this->val is assigned a non-integer type.
These two candidate functions are in the overload set:
// member function template, found by qualified lookup
template <typename T>
test::Ops::operator<<(const T&)
// non-member function template, found by ADL
template <template <typename> class E, typename T>
test::operator<<(test::Ops&, const E<T>&)
In operator lookup, no preference is given to members versus non-members. After template argument substitution, both function template specializations exactly match (with qualification conversions) the supplied argument types. But the function taking E<T> is more specialized than the one taking T, so the non-member function is chosen for this reason.
Apple clang 5.0.0 is based on LLVM clang 3.3svn. I can't find any version of LLVM clang which selects the member function. It could be a bug in Apple's code, but IMHO it's more likely to be some subtle difference in the code you are actually compiling or your environment. Have you tried compiling your example code with the suspect compiler?
Consider these two functions:
template <class Type,
class = typename std::enable_if</*HAS OPERATOR <<*/>::type>
void f(std::ostream& stream, const Type& value);
template <class Type,
class... DummyTypes,
class = typename std::enable_if<sizeof...(DummyTypes) == 0>::type>
void f(std::ostream& stream, const Type& value, DummyTypes...);
As the non-variadic overload has the priority over the variadic overload, I want to check whether the type has the operator<< with an std::ostream using std::enable_if in the first version.
So what should I write instead of /*HAS OPERATOR <<*/ ?
The following should work
template <class Type,
class = decltype(std::declval<std::ostream&>() << std::declval<Type>())>
void f(std::ostream& stream, const Type& value)
{
stream << value;
}
(note you don't need to use std::enable_if in this case)
Using trailing return type (1), you can actually have a foretaste of concepts:
template <typename Type>
auto f(std::ostream& out, Type const& t) -> decltype(out << t, void()) {
// ...
}
Because of SFINAE, this overload can only be selected if the type of out << t can be resolved, and this implies that an overload of << exists that accepts both parameters.
The one pitfall is that this does not work if you need the contrary, that is enabling a function if this overload does not exists. In this case an enable_if strategy (and the symmetric disable_if) is necessary, as far as I know.
(1) thanks to Simple for helping out with the syntax
It is easiest to check when you have the arguments around, i.e., I would rather try to use something like this:
template <typename Type>
auto f(std::ostream& out, Type const& value)
-> typename std::enable_if<sizeof(out << value) != 0>::type {
...
}
A similar effect could be obtained using std::declval() but off-hand I'm not sure about creating references.