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.
Related
Consider I have a custom type (which I can extend):
struct Foo {
int a;
string b;
};
How can I make an instance of this object assignable to a std::tie, i.e. std::tuple of references?
Foo foo = ...;
int a;
string b;
std::tie(a, b) = foo;
Failed attempts:
Overloading the assignment operator for tuple<int&,string&> = Foo is not possible, since assignment operator is one of the binary operators which have to be members of the left hand side object.
So I tried to solve this by implementing a suitable tuple-conversion operator. The following versions fail:
operator tuple<int,string>() const
operator tuple<const int&,const string&>() const
They result in an error at the assignment, telling that "operator = is not overloaded for tuple<int&,string&> = Foo". I guess this is because "conversion to any type X + deducing template parameter X for operator=" don't work together, only one of them at once.
Imperfect attempt:
Hence I tried to implement a conversion operator for the exact type of the tie:
operator tuple<int&,string&>() const Demo
operator tuple<int&,string&>() Demo
The assignment now works since types are now (after conversion) exactly the same, but this won't work for three scenarios which I'd like to support:
If the tie has variables of different but convertible types bound (i.e. change int a; to long long a; on the client side), it fails since the types have to fully match. This contradicts the usual use of assigning a tuple to a tuple of references which allows convertible types.(1)
The conversion operator needs to return a tie which has to be given lvalue references. This won't work for temporary values or const members.(2)
If the conversion operator is not const, the assignment also fails for a const Foo on the right hand side. To implement a const version of the conversion, we need to hack away const-ness of the members of the const subject. This is ugly and might be abused, resulting in undefined behavior.
I only see an alternative in providing my own tie function + class together with my "tie-able" objects, which makes me force to duplicate the functionality of std::tie which I don't like (not that I find it difficult to do so, but it feels wrong to have to do it).
I think at the end of the day, the conclusion is that this is one drawback of a library-only tuple implementation. They're not as magic as we'd like them to be.
EDIT:
As it turns out, there doesn't seem to be a real solution addressing all of the above problems. A very good answer would explain why this isn't solvable. In particular, I'd like someone to shed some light on why the "failed attempts" can't possibly work.
(1): A horrible hack is to write the conversion as a template and convert to the requested member types in the conversion operator. It's a horrible hack because I don't know where to store these converted members. In this demo I use static variables, but this is not thread-reentrant.
(2): Same hack as in (1) can be applied.
Why the current attempts fail
std::tie(a, b) produces a std::tuple<int&, string&>.
This type is not related to std::tuple<int, string> etc.
std::tuple<T...>s have several assignment-operators:
A default assignment-operator, that takes a std::tuple<T...>
A tuple-converting assignment-operator template with a type parameter pack U..., that takes a std::tuple<U...>
A pair-converting assignment-operator template with two type parameters U1, U2, that takes a std::pair<U1, U2>
For those three versions exist copy- and move-variants; add either a const& or a && to the types they take.
The assignment-operator templates have to deduce their template arguments from the function argument type (i.e. of the type of the RHS of the assignment-expression).
Without a conversion operator in Foo, none of those assignment-operators are viable for std::tie(a,b) = foo.
If you add a conversion operator to Foo,
then only the default assignment-operator becomes viable:
Template type deduction does not take user-defined conversions into account.
That is, you cannot deduce template arguments for the assignment-operator templates from the type Foo.
Since only one user-defined conversion is allowed in an implicit conversion sequence, the type the conversion operator converts to must match the type of the default assignment operator exactly. That is, it must use the exact same tuple element types as the result of std::tie.
To support conversions of the element types (e.g. assignment of Foo::a to a long), the conversion operator of Foo has to be a template:
struct Foo {
int a;
string b;
template<typename T, typename U>
operator std::tuple<T, U>();
};
However, the element types of std::tie are references.
Since you should not return a reference to a temporary,
the options for conversions inside the operator template are quite limited
(heap, type punning, static, thread local, etc).
There are only two ways you can try to go:
Use the templated assignment-operators:
You need to publicly derive from a type the templated assignment-operator matches exactly.
Use the non-templated assignment-operators:
Offer a non-explicit conversion to the type the non-templated copy-operator expects, so it will be used.
There is no third option.
In both cases, your type must contain the elements you want to assign, no way around it.
#include <iostream>
#include <tuple>
using namespace std;
struct X : tuple<int,int> {
};
struct Y {
int i;
operator tuple<int&,int&>() {return tuple<int&,int&>{i,i};}
};
int main()
{
int a, b;
tie(a, b) = make_tuple(9,9);
tie(a, b) = X{};
tie(a, b) = Y{};
cout << a << ' ' << b << '\n';
}
On coliru: http://coliru.stacked-crooked.com/a/315d4a43c62eec8d
As the other answers already explain, you have to either inherit from a tuple (in order to match the assignment operator template) or convert to the exact same tuple of references (in order to match the non-templated assignment operator taking a tuple of references of the same types).
If you'd inherit from a tuple, you'd lose the named members, i.e. foo.a is no longer possible.
In this answer, I present another option: If you're willing to pay some space overhead (constant per member), you can have both named members and tuple inheritance simultaneously by inheriting from a tuple of const references, i.e. a const tie of the object itself:
struct Foo : tuple<const int&, const string&> {
int a;
string b;
Foo(int a, string b) :
tuple{std::tie(this->a, this->b)},
a{a}, b{b}
{}
};
This "attached tie" makes it possible to assign a (non-const!) Foo to a tie of convertible component types. Since the "attached tie" is a tuple of references, it automatically assigns the current values of the members, even though you initialized it in the constructor.
Why is the "attached tie" const? Because otherwise, a const Foo could be modified via its attached tie.
Example usage with non-exact component types of the tie (note the long long vs int):
int main()
{
Foo foo(0, "bar");
foo.a = 42;
long long a;
string b;
tie(a, b) = foo;
cout << a << ' ' << b << '\n';
}
will print
42 bar
Live demo
So this solves problems 1. + 3. by introducing some space overhead.
This kind of does what you want right? (assumes that your values can be linked to the types of course...)
#include <tuple>
#include <string>
#include <iostream>
#include <functional>
using namespace std;
struct Foo {
int a;
string b;
template <template<typename ...Args> class tuple, typename ...Args>
operator tuple<Args...>() const {
return forward_as_tuple(get<Args>()...);
}
template <template<typename ...Args> class tuple, typename ...Args>
operator tuple<Args...>() {
return forward_as_tuple(get<Args>()...);
}
private:
// This is hacky, may be there is a way to avoid it...
template <typename T>
T get()
{ static typename remove_reference<T>::type i; return i; }
template <typename T>
T get() const
{ static typename remove_reference<T>::type i; return i; }
};
template <>
int&
Foo::get()
{ return a; }
template <>
string&
Foo::get()
{ return b; }
template <>
int&
Foo::get() const
{ return *const_cast<int*>(&a); }
template <>
string&
Foo::get() const
{ return *const_cast<string*>(&b); }
int main() {
Foo foo { 42, "bar" };
const Foo foo2 { 43, "gah" };
int a;
string b;
tie(a, b) = foo;
cout << a << ", " << b << endl;
tie(a, b) = foo2;
cout << a << ", " << b << endl;
}
Major downside is that each member can only be accessed by their types, now, you could potentially get around this with some other mechanism (for example, define a type per member, and wrap the reference to the type by the member type you want to access..)
Secondly the conversion operator is not explicit, it will convert to any tuple type requested (may be you don't want that..)
Major advantage is that you don't have to explicitly specify the conversion type, it's all deduced...
This code works for me. I'd love it if someone could point out anything wrong with it.
Simple Version on Compiler Explorer
More Generic Version on Compiler Explorer
#include <tuple>
#include <cassert>
struct LevelBounds final
{
int min;
int max;
operator std::tuple<int&, int&>() { return {min, max}; }
};
int main() {
int a, b;
auto lb = LevelBounds{30, 40};
std::tie(a, b) = lb;
assert(30 == a);
assert(40 == b);
return 0;
}
Consider the following classes, where the first is templated. Both are meant to hold a numeric value.
template<typename T>
struct foo
{
foo(T val) : val{ val } {}
T val;
};
struct bar
{
bar(double val) : val{ val } {}
double val;
};
I want to define a way to add these classes together to get a new one with a different value.
template<typename T>
foo<T> operator+(foo<T> a, foo<T> b)
{
return foo<T>(a.val + b.val);
}
bar operator+(bar a, bar b)
{
return bar(a.val + b.val);
}
When I use these operators with implicit conversion, the operator using object of type foo doesn't use the implicit conversion on the double value to apply my overloaded operator, even though it can do it for the non-template class. The result is that there is no operator matching the types in that expression.
int main()
{
foo<double> foo_value(11.0);
bar bar_value(11.0);
foo<double> ev1 = foo_value + 1.0; // no operator matches these operands
bar ev2 = bar_value + 1.0;
}
Does the operator have to be explicitly instantiated first? If so, a) how does that look like, and b) why isn't the instantiation done implicitly, if it can be done when initializing an object of type foo<double>?
If the standard doesn't support any sort of resolution without explicitly casting 1.0 to a value of type foo<double>, I presume the only other possibility is defining operator overloads for each type I want to use like that (for both lhs and rhs)?
The thing you have to remember about templates is that they will not do a conversion for you. All they do is try and figure out the types things are, and if that jives with template parameters, then it will stamp out the function and call it.
In you case when you do
foo_value + 1.0
the compiler goes okay, lets see if we have any operator + that will work for this. It finds
template<typename T>
foo<T> operator+(foo<T> a, foo<T> b)
{
return foo<T>(a.val + b.val);
}
and then it tries to figure out what T is so it can stamp out a concrete function. It looks at foo_value, sees it is a foo<double> so it says for the first parameter T needs to be a double. Then it looks at 1.0 and goes okay, I have a double and that is when you run into a problem. The compiler can't deduce what T should be for b because it expects a foo<some_type>, but got a double instead. Because it can't deduce a type, your code fails to compile.
In order to get the behavior you want you would need to add
template<typename T>
foo<T> operator+(foo<T> a, T b)
{
return foo<T>(a.val + b);
}
Which lets you add a T to a foo<T>, or better yet
template<typename T, typename U>
foo<T> operator+(foo<T> a, U b)
{
return foo<T>(a.val + b);
}
Which lets you add anything to a foo<T> (foo<double> + int for instance where the first version would not allow that)
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?
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.
The last question I asked was something I stumbled upon when trying to understanding another thing... that I also can't understand (not my day).
This is quite a long question statement, but at least I hope this question might prove useful to many people and not only me.
The code I have is the following:
template <typename T> class V;
template <typename T> class S;
template <typename T>
class V
{
public:
T x;
explicit V(const T & _x)
:x(_x){}
V(const S<T> & s)
:x(s.x){}
};
template <typename T>
class S
{
public:
T &x;
explicit S(V<T> & v)
:x(v.x)
{}
};
template <typename T>
V<T> operator+(const V<T> & a, const V<T> & b)
{
return V<T>(a.x + b.x);
}
int main()
{
V<float> a(1);
V<float> b(2);
S<float> c( b );
b = a + V<float>(c); // 1 -- compiles
b = a + c; // 2 -- fails
b = c; // 3 -- compiles
return 0;
}
Expressions 1 and 3 work perfectly, while expression 2 does not compile.
If I have understood properly, what happens is:
Expression 1
c is is implicitly converted to const by using a standard conversion sequence (consisting on just one qualification conversion).
V<float>(const S<T> & s) is called and a temporal the const V<float> object generated (let's call it t). It is already const-qualified because it is a temporal value.
a is converted to const similarly to c.
operator+(const V<float> & a, const V<float> & b) is called, resulting in a temporal of type const V<float> that we can call q.
the default V<float>::operator=(const & V<float>) is called.
Am I OK up to here? If I made even the most subtle mistake please, let me know, for I am trying to gain an understanding about implicit casting as deep as possible...
Expression 3
c is converted to V<float>. For that, we have a user-defined conversion sequence:
1.1. first standard conversion: S<float> to const S<float> via qualification conversion.
1.2. user-defined conversion: const S<float> to V<float> via V<float>(const S<T> & s) constructor.
1.3 second standard conversion: V<float> to const V<float> via qualification conversion.
the default V<float>::operator=(const & V<float>) is called.
Expression 2?
What I do not understand is why there is a problem with the second expression. Why is the following sequence not possible?
c is converted to V<float>. For that, we have a user-defined conversion sequence:
1.1. initial standard conversion: S<float> to const S<float> via qualification conversion.
1.2. user-defined conversion: const S<float> to V<float> via V<float>(const S<T> & s) constructor.
1.3. final standard conversion: V<float> to const V<float> via qualification conversion.
Steps 2 to 6 are the same as in case of expression 1.
After reading the C++ standard I though: 'hey! maybe the problem has to to with 13.3.3.1.2.3!' which states:
If the user-defined conversion is specified by a template conversion function, the second standard conversion sequence must have exact match rank.
But that cannot be the case since the qualification conversion has exact match rank...
I really have no clue...
Well, whether you have the answer or not, thanks you for reading up to here :)
As Edric pointed out, conversions are not considered during template argument deduction. Here, you have two contexts where the template parameter T can be deduced from the type of the arguments:
template<class T>
v<T> operator+(V<T> const&, V<T> const&);
~~~~~~~~~~~ ~~~~~~~~~~~~
But you try to invoke this function template with a V<float> on the left-hand side and an S on the right hand side. Template argument deduction results in T=float for the left hand side and you'll get an error for the right hand side because there is no T so that V<T> equals S<T>. This qualifies as a template argument deduction failure and the template is simply ignored.
If you want to allow conversions your operator+ shouldn't be a template. There is the following trick: You can define it as an inline friend inside of the class template for V:
template<class T>
class V
{
public:
V();
V(S<T> const&); // <-- note: no explicit keyword here
friend V<T> operator+(V<T> const& lhs, V<T> const& rhs) {
...
}
};
This way, the operator is not a template anymore. So, there is no need for template argument deduction and your invocation should work. The operator is found through ADL (argument dependent lookup) because the left-hand side is a V<float>. The right-hand side is properly converted to a V<float> as well.
It is also possible to disable template argument deduction for a specific argument. For example:
template<class T>
struct id {typedef T type;};
template<class T>
T clip(
typename id<T>::type min,
T value,
typename id<T>::type max )
{
if (value<min) value=min;
if (value>max) value=max;
return value;
}
int main() {
double x = 3.14;
double y = clip(1,x,3); // works, T=double
}
Even though the type of the first and last argument is an int, they are not considered during template argument deduction because id<T>::type is not a so-called *deducible context`. So, T is only deduced according to the second argument, which results in T=double with no contradictions.
When considering template matches, implicit conversions are not used. Therefore, in the following simple example:
template < typename T >
void foo( T t1, T t2 ) { /* do stuff */ }
int main( int argc, char ** argv ) {
foo( 1, 1.0 );
return 0;
}
That will not compile even though either argument could be implicitly converted to the other type (int <-> double).
Just a guess, but perhaps the compiler cannot distinguish between the conversion from V->S or from S->V while trying to figure out how to add a + c in expression 2. You're assuming the compiler will be smart enough to pick the one which allows the compilation to proceed because of the rest of the available functions, but the compiler is probably not "reading ahead" (so to speak), and is getting confused with the ambiguity of the up-conversion before trying to find the '+' operator.
Of course, if you added the compilation error, it might help clarify the problem too...