for some reason I have two classes implementing the operator "+" with templates,
(I do that because I want all children of those two classes to be able to use it).
I have come down to a very simple code implementing what I would like to use:
#include <type_traits>
class A{};
template<typename T>
A operator+(T& lhs,int rhs){
static_assert(std::is_base_of<A, T>::value, "T must inherit from A");
A to_return;
return to_return;
}
class B{};
template<typename T>
B operator+(T& lhs,int rhs){
static_assert(std::is_base_of<B, T>::value, "T must inherit from B");
B to_return;
return to_return;
}
int main()
{
A u;
A v = u+1;
}
When compiling, the compiler (g++ or intel) returns the following error :
g++ : main.cpp:25:11: error: ambiguous overload for 'operator+' in 'u + 1'
main.cpp:25:11: note: candidates are:
main.cpp:6:3: note: A operator+(T&, int) [with T = A]
main.cpp:15:3: note: B operator+(T&, int) [with T = A]
icpc : main.cpp(25): error: more than one operator "+" matches these operands:
function template "A operator+(T &, int)"
function template "B operator+(T &, int)"
operand types are: A + int
A v = u+1;
^
Although it is not that ambiguous as v should be of type A, so only the first template should work.
Any idea to get around this keeping the two templates operators ?
Or another idea to have an operator working for all children of A and B ?
I.e. for all classes C child of A, I would like to be able to write
A w = u + 1; //where u is of type C.
And same for B.
Thank you,
Tony
EDIT:
Following the answer given by Barry, std::enable_if does the job. However, it turns out that what I needed exactly was to use two typenames, the technique proposed by Barry has to be slightly modified to add this possibility:
#include <type_traits>
#include <iostream>
class A{};
template<typename T1,typename T2 = typename std::enable_if<std::is_base_of<A,T1>::value, A>::type>
A operator+(T1& lhs,T2& rhs){
A to_return;
return to_return;
}
class B{};
template<typename T1,typename T2 = typename std::enable_if<std::is_base_of<B,T1>::value, B>::type>
B operator+(T2& lhs,T2& rhs){
B to_return;
return to_return;
}
int main()
{
A u;
A w = u+u;
}
Then it works fine, even if T1 and T2 are different children of A.
Overload resolution is solely based on the function signature, which is its name, its cv-qualifications, and its parameter types.
For your first one, that is:
operator+(T& lhs, int rhs);
And for your second one, that is also:
operator+(T& lhs, int rhs);
Since those are identical, the compiler can't distinguish between the two - hence the ambiguity. One way around this is to move your static assert into the return type and use SFINAE:
template<typename T>
typename std::enable_if<
std::is_base_of<A, T>::value,
A
>::type
operator+(T& lhs,int rhs){
// stuff
}
And the same for your other operator. This will work until you try it with some T that derives from both, and then it will become ambiguous again.
Or, depending on what you're actually doing with lhs, simply:
A operator+(A& lhs, int rhs); // already accepts anything that derives from A
Related
Say I want two orthogonal types A and B so that I can write
A a = b1 * b2; // b1,b2 of type B
B b = a1 * a2; // a1,a2 of type A
The data they share is the same, so I attempted some policy-based design. Some code:
#include <type_traits>
struct isA {};
struct isB {};
template<typename T>
struct myClass
{
int _data;
template<typename U>
myClass<U> operator * ( const myClass<T>& other );
};
template<typename T>
template<typename U>
myClass<U> myClass<T>::operator * ( const myClass<T>& other )
{
// just an idea, will not be needed if correct instanciation
static_assert( std::is_same<U,T>::value, "cannot return same type" );
// ... here, some code
}
int main()
{
myClass<isA> a1,a2;
myClass<isB> b = a1 * a2;
}
This fails with:
main.cpp: In function 'int main()':
main.cpp:26:25: error: no match for 'operator*' (operand types are
'myClass<isA>' and 'myClass<isA>')
myClass<isB> b = a1 * a2;
main.cpp:12:16: note: candidate: 'template<class U> myClass<U> myClass<T>::operator*(const myClass<T>&) [with U = U; T = isA]'
myClass<U> operator * ( const myClass<T>& other );
main.cpp:12:16: note: template argument deduction/substitution failed:
main.cpp:26:27: note: couldn't deduce template parameter 'U'
What I understand is that it fails because its only the function arguments that are used by the compiler to generate the instanciation, not the return type. Thus the compiler cannot generate the correct instanciation for the operator.
My question (pretty simple): how can I implement this operator ?
There is no template specialization required here, the behavior is the same with the two types (but other functions - not shown here - will have a specific implementation for each of the types). But I want to enforce the fact that that you cannot do: A a = a1 * a2;
Side note: couldn't find any question with this topic, if you find one, please link!
You can implement it as two (non-template) free functions. If the implementation is exactly the same, these can specify the return type for a shared implementation.
namespace detail
{
template<typename Out, typename In>
MyClass<Out> times( const MyClass<In> & lhs, const MyClass<In> & rhs)
{
// shared code here
}
}
myClass<isA> operator * ( const myClass<isB>& lhs, const myClass<isB>& rhs )
{ return detail::times<isA>(lhs, rhs); }
myClass<isB> operator * ( const myClass<isA>& lhs, const myClass<isA>& rhs )
{ return detail::times<isB>(lhs, rhs); }
You can create a trait that maps isA to isB, and isB to isA.
namespace detail
{
template<typename>
struct myClassTraits;
template<>
struct myClassTraits<isA>
{
using other_type = isB;
};
template<>
struct myClassTraits<isB>
{
using other_type = isA;
};
}
template<typename T>
struct myClass
{
int _data;
using times_t = myClass<typename detail::myClassTraits<T>::other_type>;
times_t operator * ( const myClass& other );
};
Unfortunately C++ doesn't use return type to deduce template parameters (some other languages can do it), so you can do nothing with the template.
However to make
A a = b1 * b2; // b1,b2 of type B
working you may implement implicit conversion constructor so that first you'll get type B as a result of multiplication operator and then it will be cast to A type:
template <typename U>
myClass(const myClass<U>& other) {} // copy conversion constructor
template <typename U>
myClass(myClass<U>&& other) {} // move conversion constructor
so that
A a = b1 * b2;
will be equivalent to
A a = A(b1 * b2);
Since std::to_string is added to c++11, I started implementing to_string instead of the more traditional operator<<(ostream&, T). I need to link the two together in order to incorporate libraries that rely on operator<<(ostream&, T). I want to be able to express that if T has operator<<(ostream&, T), use it; otherwise, use std::to_string. I am prototyping a more limited version that enables operator<< for all enum classes.
enum class MyEnum { A, B, C };
// plain version works
//std::ostream& operator<<(std::ostream& out, const MyEnum& t) {
// return (out << to_string(t));
//}
// templated version not working
template<typename T,
typename std::enable_if<std::is_enum<T>::value>::type
>
std::ostream& operator<<(std::ostream& out, const T& t) {
return (out << to_string(t));
}
Compiler says error: no match for 'operator<<' (operand types are 'std::ostream {aka std::basic_ostream<char>}' and 'MyEnum')
cout << v << endl;
Questions:
Why is the template function not found by the compiler?
Is there a way to implement the general solution?
The following will work if there exists a std::to_string that accepts an argument of type const MyEnum (which doesn't exist according to clang-703.0.31).
#include <iostream>
#include <type_traits>
enum class MyEnum { A, B, C };
template<typename T>
typename std::enable_if<std::is_enum<T>::value, std::ostream &>::type
operator<<(std::ostream& out, const T& t) {
return (out << std::to_string(t));
}
int main() {
MyEnum a;
std::cout << a << std::endl;
}
According to the docs, there are two ways of using std::enable_if, one of which is to make the return type of a function only valid if T is an enum type (in your case). This is what this code shows. If std::is_enum<T>::value is true, then std::enable_if<std::is_enum<T>::value, std::ostream &>::type results in std::ostream & and is not defined (which makes the compiler yell at you) otherwise.
You could probably write something like my::to_string which would try to convert to a string user-defined types as well:
namespace my {
template<typename T>
typename std::enable_if<! std::is_void<T>{} && std::is_fundamental<T>{}, std::string>::type
to_string(T t) {
return std::to_string(t);
}
std::string to_string(std::nullptr_t) = delete;
std::string to_string(MyEnum a) {
return "This is an enum";
}
}
Then, you can use my::to_string instead of std::to_string in your operator<<:
return (out << my::to_string(t));
EDIT: using my::to_string now results in a compilation error when it's argument is void or std::nullptr_t.
Why your code didn't work
Look at the example in the docs:
// 2. the second template argument is only valid if T is an integral type:
template < class T,
class = typename std::enable_if<std::is_integral<T>::value>::type>
bool is_even (T i) {return !bool(i%2);}
As you can see, the second template argument is written with class = /* enable_if stuff */, but in your code you do simply template< typename T, /* enable_if stuff */ >. So, if you follow the docs, you'll get the correct result - the compiler will say it couldn't find a specialization of std::to_string that accepted an enum as a parameter.
This is a follow-up of Explicit ref-qualified conversion operator templates in action. I have experimented with many different options and I am giving some results here in an attempt to see if there is any solution eventually.
Say a class (e.g. any) needs to provide conversion to any possible type in a convenient, safe (no surprises) way that preserves move semantics. I can think of four different ways.
struct A
{
// explicit conversion operators (nice, safe?)
template<typename T> explicit operator T&& () &&;
template<typename T> explicit operator T& () &;
template<typename T> explicit operator const T& () const&;
// explicit member function (ugly, safe)
template<typename T> T&& cast() &&;
template<typename T> T& cast() &;
template<typename T> const T& cast() const&;
};
// explicit non-member function (ugly, safe)
template<typename T> T&& cast(A&&);
template<typename T> T& cast(A&);
template<typename T> const T& cast(const A&);
struct B
{
// implicit conversion operators (nice, dangerous)
template<typename T> operator T&& () &&;
template<typename T> operator T& () &;
template<typename T> operator const T& () const&;
};
The most problematic cases are to initialize an object or an rvalue reference to an object, given a temporary or an rvalue reference. Function calls work in all cases (I think) but I find them too verbose:
A a;
B b;
struct C {};
C member_move = std::move(a).cast<C>(); // U1. (ugly) OK
C member_temp = A{}.cast<C>(); // (same)
C non_member_move(cast<C>(std::move(a))); // U2. (ugly) OK
C non_member_temp(cast<C>(A{})); // (same)
So, I next experiment with conversion operators:
C direct_move_expl(std::move(a)); // 1. call to constructor of C ambiguous
C direct_temp_expl(A{}); // (same)
C direct_move_impl(std::move(b)); // 2. call to constructor of C ambiguous
C direct_temp_impl(B{}); // (same)
C copy_move_expl = std::move(a); // 3. no viable conversion from A to C
C copy_temp_expl = A{}; // (same)
C copy_move_impl = std::move(b); // 4. OK
C copy_temp_impl = B{}; // (same)
It appears that the const& overload is callable on an rvalue, which gives ambiguities, leaving copy-initialization with an implicit conversion as the only option.
However, consider the following less trivial class:
template<typename T>
struct flexi
{
static constexpr bool all() { return true; }
template<typename A, typename... B>
static constexpr bool all(A a, B... b) { return a && all(b...); }
template<typename... A>
using convert_only = typename std::enable_if<
all(std::is_convertible<A, T>{}...),
int>::type;
template<typename... A>
using explicit_only = typename std::enable_if<
!all(std::is_convertible<A, T>{}...) &&
all(std::is_constructible<T, A>{}...),
int>::type;
template<typename... A, convert_only<A...> = 0>
flexi(A&&...);
template<typename... A, explicit_only<A...> = 0>
explicit flexi(A&&...);
};
using D = flexi<int>;
which provides generic implicit or explicit constructors depending on whether the input arguments can be implicitly or explicitly converted to a certain type. Such logic is not that exotic, e.g. some implementation of std::tuple can be like that. Now, initializing a D gives
D direct_move_expl_flexi(std::move(a)); // F1. call to constructor of D ambiguous
D direct_temp_expl_flexi(A{}); // (same)
D direct_move_impl_flexi(std::move(b)); // F2. OK
D direct_temp_impl_flexi(B{}); // (same)
D copy_move_expl_flexi = std::move(a); // F3. no viable conversion from A to D
D copy_temp_expl_flexi = A{}; // (same)
D copy_move_impl_flexi = std::move(b); // F4. conversion from B to D ambiguous
D copy_temp_impl_flexi = B{}; // (same)
For different reasons, the only available option direct-initialization with an implicit conversion. However, this is exactly where implicit conversion is dangerous. b might actually contain a D, which may be a kind of container, yet the working combination is invoking D's constructor as an exact match, where b behaves like a fake element of the container, causing a runtime error or disaster.
Finally, let's try to initialize an rvalue reference:
D&& ref_direct_move_expl_flexi(std::move(a)); // R1. OK
D&& ref_direct_temp_expl_flexi(A{}); // (same)
D&& ref_direct_move_impl_flexi(std::move(b)); // R2. initialization of D&& from B ambiguous
D&& ref_direct_temp_impl_flexi(B{}); // (same)
D&& ref_copy_move_expl_flexi(std::move(a)); // R3. OK
D&& ref_copy_temp_expl_flexi(A{}); // (same)
D&& ref_copy_move_impl_flexi = std::move(b); // R4. initialization of D&& from B ambiguous
D&& ref_copy_temp_impl_flexi = B{}; // (same)
It appears that every use case has its own requirements and there is no combination that might work in all cases.
What's worse, all above results are with clang 3.3; other compilers and versions give slightly different results, again with no universal solution. For instance: live example.
So: is there any chance something might work as desired or should I give up conversion operators and stick with explicit function calls?
The C++ standard unfortunately does not have any special rule to resolve this particular ambiguity. The problem come from the fact that you are trying to overload on 2 different things: the type that the compiler is trying to convert to; and the kind of reference from which you are trying to convert from.
By introducing proxy classes, you can split the resolution in 2 steps. Step 1: decide if it's an r-value reference, an l-value reference, or a const l-value reference. Step 2: convert to any type, keeping the decision made in step 1 about the kind of reference. That way, you can use your solution with a cast() function but save you from having to specify the type:
struct A
{
class A_r_ref
{
A* a_;
public:
A_r_ref(A* a) : a_(a) {}
template <typename T> operator T&&() const&&;
};
struct A_ref
{
A* a_;
public:
A_ref(A* a) : a_(a) {}
template <typename T> operator T&() const&&;
};
struct A_const_ref
{
const A* a_;
public:
A_const_ref(const A* a) : a_(a) {}
template <typename T> operator const T&() const&&;
};
A_r_ref cast() && { return A_r_ref(this); }
A_ref cast() & { return A_ref(this); }
A_const_ref cast() const& { return A_const_ref(this); }
};
When I compile the following snippet, I get a compiler error with clang, but not with g++/MSVC:
#include <string>
template<typename T> struct Const {
explicit Const(T val) : value(val) {}
T value;
};
template<typename T> struct Var {
explicit Var(const std::string &n) : name(n) {}
std::string name;
};
template<typename L, typename R> struct Greater {
Greater(L lhs, R rhs) : left(lhs), right(rhs) {}
L left;
R right;
};
template<typename L>
Greater<L, Const<int> > operator > (L lhs, int rhs) {
return Greater<L, Const<int> >(lhs, Const<int>(rhs));
}
template<typename R>
Greater<Const<int>, R> operator > (int lhs, R rhs) {
return Greater<Const<int>, R>(Const<int>(lhs), rhs);
}
Var<double> d("d");
int main() {
d > 10;
return 0;
}
The error reported is the following:
error: overloaded 'operator>' must have at least one parameter of
class or enumeration type
Greater<Const<int>, R> operator > (int lhs, R rhs) {
^
./val.h:31:24: note: in instantiation of function template specialization
'operator><int>' requested here
Greater<Const<int>, R> operator > (int lhs, R rhs) {
^
1 error generated.
which is about the operator function that is not used. If, instead, I write 10 > d instead of d > 10, then I get the same error about the other operator > function. The above compiles fine under gcc 4.4.6 and VS2012. What is my mistake ?
Thank you.
Clang is right: operator overloading requires at least one class or enum type parameter, otherwise the program is ill-formed (13.5/1). To see why this error even appears, we have to parse some more Standard legalese.
Recall the Holy Trinity of Name Lookup, Argument Deduction and Overload Resolution. The first step finds two overloaded operator>. The second step deduces template arguments for each version. You might think that the second overload would fall victim to the SFINAE rule (14.8.2), so that only the first survives to the third step. However, there is no substition failure (as in e.g. a missing nested typedef), but an illegal construct (see the earlier mentioned 13.5/1). That itself renders the program ill-formed (14.3/6)
6 If the use of a template-argument gives rise to an ill-formed construct in the instantiation of a template specialization, the program is ill-formed.
In 14.8.3 it is mentioned that this check on the deduced arguments happens before overload resolution, so your preferred operator has no chance of being selected.
As a C++03 work-around, you could define two friend non-template operator> inside your Var<T> class template. These would be injected into the surrounding (global, in this example) namespace as non-template functions with one class type parameter, so the above error should not occur.
I must admit, I don't really know why clang complains here, it looks like a bug (of the compiler). Btw, clang 3.3 also exhibits the problem.
You can suppress it using SFINAE:
template<typename L>
typename std::enable_if<std::is_class<L>::value || std::is_enum<L>::value,
Greater<L, Const<int>>>::type
operator > (L lhs, int rhs) {
return Greater<L, Const<int> >(lhs, Const<int>(rhs));
}
template<typename R>
typename std::enable_if<std::is_class<R>::value || std::is_enum<R>::value,
Greater<Const<int>,R>>::type
operator > (int lhs, R rhs) {
return Greater<Const<int>, R>(Const<int>(lhs), rhs);
}
This looks like a bug in g++ and VS to me. In your example, your type R is int (because the right-hand operand is int). This then makes the signature of the function Greater<Const<int>, R> operator > (int lhs, int rhs) which is the same (parameter) signature as the builtin operator< for ints. Note that it has to consider both templates (and attempt to deduce types separately for each) when deciding which operator> to use: It can't just look at one of them and decide to ignore the other one.
I would like to know why implicit type conversion doesn't work with outside operator overloading on class templates. Here is the working, non-templated version:
class foo
{
public:
foo() = default;
foo(int that)
{}
foo& operator +=(foo rhs)
{
return *this;
}
};
foo operator +(foo lhs, foo rhs)
{
lhs += rhs;
return lhs;
}
As expected, the following lines compile correctly:
foo f, g;
f = f + g; // OK
f += 5; // OK
f = f + 5; // OK
f = 5 + f; // OK
On the other hand, when class foo is declared as a simple template like this:
template< typename T >
class foo
{
public:
foo() = default;
foo(int that)
{}
foo& operator +=(foo rhs)
{
return *this;
}
};
template< typename T >
foo< T > operator +(foo< T > lhs, foo< T > rhs)
{
lhs += rhs;
return lhs;
}
The following lines compile with errors:
foo< int > f, g;
f = f + g; // OK
f += 5; // OK
f = f + 5; // Error (no match for operator+)
f = 5 + f; // Error (no match for operator+)
I would like to understand why the compiler (GCC 4.6.2) is unable to perform implicit type conversion using the converting constructor for the template version of the class. Is that the expected behaviour? Apart from manually creating all the necessary overloads, is there any workaround for this?
The reason it does not just work is that implicit type conversions (that is, via constructors) do not apply during template argument deduction.
But it works if you make the outside operator a friend since then the type T is know, allowing the compiler to investigate what can be casted to make the arguments match.
I made an example based on yours (but removed C++11 stuff), inspired by Item 46 (a rational number class) in Scott Meyers Effective C++ (ed 3). Your question is almost an exact match to that item. Scott also notes that ... "this use of friend is not related to the access of non-public parts of the class."
This will also allow work with mixes of foo< T >, foo< U > etc as long as T and U can be added etc.
Also look at this post: C++ addition overload ambiguity
#include <iostream>
using namespace std;
template< class T >
class foo
{
private:
T _value;
public:
foo() : _value() {}
template <class U>
foo(const foo<U>& that) : _value(that.getval()) {}
// I'm sure this it can be done without this being public also;
T getval() const { return _value ; };
foo(const T& that) : _value(that) {}
friend const foo operator +(foo &lhs,const foo &rhs)
{
foo result(lhs._value+rhs._value);
return result;
};
friend const foo operator +(foo &lhs,const T &rhsval)
{
foo result(lhs._value+rhsval);
return result;
};
friend const foo operator +(const T &lhsval,foo &rhs)
{
foo result(lhsval+rhs._value);
return result;
};
friend foo& operator +=(foo &lhs,const foo &rhs)
{
lhs._value+=rhs._value;
return lhs;
};
friend std::ostream& operator<<(std::ostream& out, const foo& me){
return out <<me._value;
}
};
int main(){
foo< int > f, g;
foo< double > dd;
cout <<f<<endl;
f = f + g;
cout <<f<<endl;
f += 3 ;
cout <<f<<endl;
f = f + 5;
cout <<f<<endl;
f = 7 + f;
cout <<f<<endl;
dd=dd+f;
cout <<dd<<endl;
dd=f+dd;
cout <<dd<<endl;
dd=dd+7.3;
cout <<dd<<endl;
}
I put this question to the library authors at MS and got an extremely informative response from Stephan Lavavej, so I give him full credit for this information.
The compile error you get in the template case is due to the fact that template argument deduction runs before overload resolution, and template argument deduction needs exact matches to add anything to the overload set.
In detail, template argument deduction looks at each pair of parameter type P and argument type A, and tries to find template substitutions that will make A exactly match P. After finding matches for each argument, it checks for consistency (so that if you call bar(foo<T>, foo<T>) with T=int for the first parameter and T=double as the second, it also fails). Only after exact, consistent matches are successfully substituted in the function signature is that signature added to the set of candidate functions for overload resolution.
Only after all ordinary functions (found through name lookup) and matching function template signatures have been added to the overload set is overload resolution run, at which point all of these function signatures are evaluated for a "best match", during which time implicit conversions will be considered.
For the operator+(foo<T>, foo<T>) case with foo<int> + 5, template argument deduction can find no substitution for T that will make the expression foo<T> exactly match int, so that overload of operator+ gets tossed out as a candidate and the implicit conversion is never even seen.
The opinion here seems to be that this is generally a good thing, as it makes templates much more predictable, leaving the realm of strange implicit behaviors to overload resolution.
The standard has plenty to say about this at:
14.8.2.1 Deducing template arguments from a function call
"Template argument deduction is done by comparing each function template parameter type (call it P) with
the type of the corresponding argument of the call (call it A) as described below. ...
... In general, the deduction process attempts to find template argument values that will make the deduced A
identical to A (after the type A is transformed as described above)"
It goes on to list a few special cases where this rule has exceptions involving cv-qualifiers (so T& will be compatible with const T&), and matching of derived classes (it can in some cases match Derived& to Base&) but basically, exact matching is the rule.
All possible foo<T> are equally valid conversions from int since the constructor takes int, not the template type. The compiler isn't able to use the other parameter in the operator to guess which one you might mean, so you get the error. If you explicitly tell it which instantiation you want I believe it would work.