这是一个很长的问题陈述,但至少我希望这个问题可能对许多人有用,而不仅仅是我.
我的代码如下:
- 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;
- }
表达式1和3完美地工作,而表达式2不能编译.
如果我理解得当,会发生什么:
表达1
> c通过使用标准转换序列(仅包含一个限定转换)隐式转换为const.
调用> V< float>(const S< T>& s)并且使用时间const V< float>生成的对象(让我们称之为t).它已经是const限定的,因为它是一个时间值.
> a与c类似地转换为const.
调用>运算符(const V< float>& a,const V< float>& b),得到const V< float>类型的时间.我们可以打电话给q.
>调用默认的V< float> :: operator =(const& V< float>).
我可以到这儿了吗?如果我做出了最微妙的错误,请告诉我,因为我试图尽可能深入了解隐式铸造……
表达3
> c被转换为V< float>.为此,我们有一个用户定义的转换序列:
1.1.第一标准转换:S< float>到const S< float>通过资格转换.
1.2.用户定义的转换:const S< float>到V< float>通过V< float>(const S< T>& s)构造函数.
1.3秒标准转换:V< float>到const V< float>通过资格转换.
>调用默认的V< float> :: operator =(const& V< float>).
表达2?
我不明白的是为什么第二个表达式存在问题.为什么以下顺序不可能?
> c被转换为V< float>.为此,我们有一个用户定义的转换序列:
1.1.初始标准转换:S< float>到const S< float>通过资格转换.
1.2.用户定义的转换:const S< float>到V< float>通过V< float>(const S< T>& s)构造函数.
1.3.最终标准转换:V< float>到const V< float>通过资格转换.
>步骤2至6与表达式1的情况相同.
读完C标准后我说:’嘿!也许问题必须与13.3.3.1.2.3!’其中说明:
If the user-defined conversion is specified by a template conversion function,the second standard conversion sequence must have exact match rank.
但情况并非如此,因为资格转换具有完全匹配等级……
我真的不知道……
好吧,不管你有没有答案,谢谢你在这里阅读:)
解决方法
- template<class T>
- v<T> operator+(V<T> const&,V<T> const&);
- ~~~~~~~~~~~ ~~~~~~~~~~~~
但是您尝试使用V< float>调用此函数模板在左侧,S在右侧.模板参数推导导致左侧T =浮动,右侧会出现错误,因为没有T,因此V< T>.等于S< T>.这有资格作为模板参数推导失败,简单地忽略模板.
如果您想允许转换,则您的操作符不应该是模板.有以下技巧:您可以将其定义为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) {
- ...
- }
- };
这样,运算符就不再是模板了.因此,不需要模板参数推导,您的调用应该起作用.通过ADL(参数相关查找)找到运算符,因为左侧是V< float>.右侧适当地转换为V< float>同样.
也可以禁用特定参数的模板参数推断.例如:
- 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
- }
即使第一个和最后一个参数的类型是int,但在模板参数推断期间不考虑它们,因为id< T> :: type不是所谓的* deducible context`.所以,T只是根据第二个参数推导出来,这导致T = double而没有矛盾.