Strange error with a templated operator overload - c++

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.

Related

Allow implicit type conversion during template evaluation

I'm writing a wrapper class for C++ types, which allows me to instrument when a wrapped object is constructed, accessed, modified, and destroyed. To make this transparent for the original code, I include implicit conversion functions back to the underlying type, but this fails when a wrapped object is passed directly to a template since implicit conversions aren't evaluated. Here's some code that demonstrates this problem:
#include <utility>
// simplified wrapper class
template <typename T>
class wrap {
T t;
public:
wrap() : t() {}
wrap(const T& _t) : t(_t) {}
wrap(T&& _t) : t(std::move(_t)) {}
constexpr operator T&() { return t; }
constexpr operator const T&() const { return t; }
};
// an example templated function
template <typename T>
bool is_same(const T& t1, const T& t2) { return t1 == t2;}
// second invocation fails due to template substitution failure
bool problem() {
wrap<int> w(5);
return is_same(static_cast<int>(w), 5) && is_same<>(w, 5);
}
I can resolve this manually by performing a static_cast on the wrapped variable at each template call site (as shown in the first invocation), but this doesn't scale well since I'm working with a large code base. Similar questions suggest inlining each template function as a friend function, but this also requires identifying and copying each template, which doesn't scale.
I'd appreciate any advice on how to (1) workaround this conversion problem with templated functions, or (2) otherwise instrument a variable at source-level without this problem.
The fault in this example lies with is_same. It declares that it requires two arguments of the same type, which is a requirement it does not need, and fails to require that type to have an ==, which it does need.
Granted, it is common to find C++ that poorly constrains template functions because it is difficult and verbose to do otherwise. Authors take a practical shortcut. That said, isn't the approach to fix the interface of is_same?
// C++17 version. Close to std::equal_to<>::operator().
template <typename T, typename U>
constexpr auto is_same(T&& t, U&& u)
noexcept(noexcept(std::forward<T>(t) == std::forward<U>(u)))
-> decltype(std::forward<T>(t) == std::forward<U>(u))
{
return std::forward<T>(t) == std::forward<U>(u);
}
With a corrected is_same, the code just works.
There are other examples one can imagine which may require two arguments to have the same type. For example, if the return type depends on the argument type and the return value can come from either:
template <typename T>
T& choose(bool choose_left, T& left, T& right) {
return choose_left ? left : right;
}
This is much rarer. But it might actually require thought to decide whether to use the underlying or wrapper type. If you have this enhanced behavior in the wrapper type, and conditionally use a wrapped value or an underlying value, should the underlying value be wrapped to continue to get the enhanced behavior, or do we drop the enhancement? Even if you could make this silently choose one of those two behaviors, would you want to?
However, you can still make it easier to get the value than to say static_cast<T>(...), for example by providing an accessor:
// given wrap<int> w and int i
is_same(w.value(), 5);
choose_left(true, w.value(), i);
I have a few other important comments:
wrap() : t() {}
This requires T be default constructible. = default does the right thing.
wrap(const T& _t) : t(_t) {}
wrap(T&& _t) : t(std::move(_t)) {}
These are not explicit. A T is implicitly convertible to a wrap<T> and vice versa. This does not work well in C++. For example, true ? w : i is not well-formed. This causes std::equality_comparable_with<int, wrap<int>> to be false, which would be a reasonable requirement for is_same. Wrapper types should probably be explicitly constructed, and can be implicitly converted to the underlying type.
constexpr operator T&() { return t; }
constexpr operator const T&() const { return t; }
These are not ref-qualified, so they return lvalue references even if the wrapper is an rvalue. That seems ill-advised.
Finally, construction and conversion only take into account the exact type T. But any place T is used, it might be implicitly converted from some other type. And two conversions are disallowed in C++. So for a wrapper type, one has a decision to make, and that often means allowing construction from anything a T is constructible from.
With a pointer wrapper function it can work, if you treat the "inner" guy as a pointer.
This is not a complete solution, but should be a good starting point for you (for instance, you need to carefully write the copy and move ctors).
You can play with this code in here.
Disclaimer: I took the idea from Andrei Alexandrescu from this presentation.
#include <iostream>
using namespace std;
template <typename T>
class WrapperPtr
{
T * ptr;
public:
WrapperPtr(const WrapperPtr&){
// ???
}
WrapperPtr(WrapperPtr&&) {
// ???
}
WrapperPtr(const T & other)
: ptr(new T(other)) {}
WrapperPtr(T * other)
: ptr(other) {}
~WrapperPtr()
{
// ????
delete ptr;
}
T * operator -> () { return ptr; }
T & operator * () { return *ptr; }
const T & operator * () const { return *ptr; }
bool operator == (T other) const { other == *ptr; }
operator T () { return *ptr; }
};
// an example templated function
template <typename T>
bool my_is_same(const T& t1, const T& t2) { return t1 == t2;}
bool problem() {
WrapperPtr<int> w(5);
return my_is_same(static_cast<int>(w), 5) && my_is_same(*w, 5);
}

Decltype cast operator in Visual Studio

Consider following code example:
#include <algorithm>
template<class T, class U>
struct lazy_caller{
lazy_caller(T &&one, U &&two) : m_one(one), m_two(two){}
operator decltype(std::max(T(), U()))() const {
return std::max(m_one, m_two);
}
T m_one;
U m_two;
};
int main()
{
lazy_caller<int, int> caller(1, 2);
int i = caller;
return 0;
}
As you may imagine, in real code I wanted to do more sophisticated type deduction to create appropriate conversion operator. Anyway - this code does not compile in VS2017 (and I guess the same is with earlier ones) - and so I would like to ask if there is any workaround for this issue? I have already tried:
operator auto () const
It also generates compiler errors like:
source_file.cpp(8): error C2833: 'operator function-style cast' is not a recognized operator or type
Is there any solution for this problem with msvc?
Because gcc has not problem with neither operator auto nor operator decltype(..).
You were very close with operator auto () const. This suggested workaround involves using C++11 function syntax:
operator auto() const -> decltype(std::max(T(), U())) {
return std::max(m_one, m_two);
}
Is there any solution for this problem with msvc?
What about passing through an using alias?
using maxType = decltype(std::max(T(), U()));
operator maxType () const {
return std::max(m_one, m_two);
}
or, maybe better, using std::declval(),
using maxType = decltype(std::max(std::declval<T>(), std::declval<U>()));
If this doesn't work, you can try with a third defaulted template type for the lazy_caller class; something like
template <typename T, typename U,
typename R = decltype(std::max(std::declval<T>(), std::declval<U>()))>
struct lazy_caller
{
lazy_caller(T && one, U && two) : m_one(one), m_two(two)
{ }
operator R () const
{ return std::max(m_one, m_two); }
T m_one;
U m_two;
};

clang rejects a template `/` operator but gnu c++ accepts it

In the context of unit management for scientific programming, I am managing the following class:
template <class UnitName>
class Quantity
{
double value;
public:
Quantity(double val = 0) : value(val) {}
Quantity(const Quantity &) {}
Quantity & operator = (const Quantity &) { return *this; }
double get_value() const noexcept { return value; }
operator double() const noexcept { return value; }
template <class SrcUnit>
Quantity(const Quantity<SrcUnit> &)
{
// here the conversion is done
}
template <class SrcUnit>
Quantity & operator = (const Quantity<SrcUnit> &)
{
// here the conversion is done
return *this;
}
template <class TgtUnit> operator TgtUnit() const
{
TgtUnit ret;
// here the conversion is done
return ret;
}
template <class U, class Ur>
Quantity<Ur> operator / (const Quantity<U> & rhs) const
{
return Quantity<Ur>(value / rhs.value);
}
};
Although the class is much more complex, I think I put enough information in order to describe my problem:
Now consider the following code snippet:
struct km_h {};
struct Km {};
struct Hour {};
Quantity<km_h> compute_speed(const Quantity<Km> & dist,
const Quantity<Hour> & time)
{
Quantity<km_h> v = dist/time;
return v;
}
This code is accepted by gnu c++ compiler and it runs well. The last template operator / is called.
But it is rejected by clang++ compiler (v 3.8.1) with the following message:
test-simple.cc:53:26: error: use of overloaded operator '/' is ambiguous (with operand
types 'const Quantity<Km>' and 'const Quantity<Hour>')
Quantity<km_h> v = dist/time;
~~~~^~~~~
test-simple.cc:53:26: note: built-in candidate operator/(__int128, unsigned long long)
test-simple.cc:53:26: note: built-in candidate operator/(unsigned long, long double)
So my questions would be: why clang++ rejects it? is a valid code? or gnu c++ should reject it?
In the case where the code would be valid, how could modify it in order to clang++ accept it?
I believe that clang is right to reject your code†, but gcc doesn't actually do what you want (both dist and time are implicitly convertible to double‡ and gcc believes the builtin operator/(double, double) is the best viable candidate). The problem is, you wrote:
template <class U, class Ur>
Quantity<Ur> operator / (const Quantity<U> & rhs) const
What is Ur? It's a non-deduced context - so attempting to invoke this operator as simply dist / time is a deduction failure. Your candidate is never considered. In order to actually use it, you'd have to explicitly provide Ur like so:
dist.operator/<Hour, km_h>(time); // explicitly providing Ur == km_h
Since that's awful, you can't have Ur be deduced as a template argument - you have to provide it yourself as some metafunction of the two units:
template <class U>
Quantity<some_mf_t<UnitName, U>> operator/(Quantity<U> const& ) const;
with some_mf_t is to be defined.
†You have both operator double() and template <class T> operator T(), which means that all the builtin operator/s are equally viable candidates (they're all non-template, exact matches).
‡Having operator double() sort of defeats the purpose of writing type safe units, no?

C++ template, ambiguous overload

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

Implicit conversion when overloading operators for template classes

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.