c – 没有发生隐式转换

前端之家收集整理的这篇文章主要介绍了c – 没有发生隐式转换前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我问的最后一个问题是我在试图理解另一件事时偶然发现的事情……我也无法理解(不是我的一天).

这是一个很长的问题陈述,但至少我希望这个问题可能对许多人有用,而不仅仅是我.

我的代码如下:

  1. template <typename T> class V;
  2. template <typename T> class S;
  3.  
  4. template <typename T>
  5. class V
  6. {
  7. public:
  8. T x;
  9.  
  10. explicit V(const T & _x)
  11. :x(_x){}
  12.  
  13. V(const S<T> & s)
  14. :x(s.x){}
  15. };
  16.  
  17. template <typename T>
  18. class S
  19. {
  20. public:
  21. T &x;
  22.  
  23. explicit S(V<T> & v)
  24. :x(v.x)
  25. {}
  26. };
  27.  
  28. template <typename T>
  29. V<T> operator+(const V<T> & a,const V<T> & b)
  30. {
  31. return V<T>(a.x + b.x);
  32. }
  33.  
  34. int main()
  35. {
  36. V<float> a(1);
  37. V<float> b(2);
  38. S<float> c( b );
  39.  
  40. b = a + V<float>(c); // 1 -- compiles
  41. b = a + c; // 2 -- fails
  42. b = c; // 3 -- compiles
  43.  
  44. return 0;
  45. }

表达式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.

但情况并非如此,因为资格转换具有完全匹配等级……

我真的不知道……

好吧,不管你有没有答案,谢谢你在这里阅读:)

解决方法

正如Edric所指出的那样,在模板参数推导期间不会考虑转换.在这里,您有两个上下文,其中模板参数T可以从参数的类型推导出来:
  1. template<class T>
  2. v<T> operator+(V<T> const&,V<T> const&);
  3. ~~~~~~~~~~~ ~~~~~~~~~~~~

但是您尝试使用V< float>调用函数模板在左侧,S在右侧.模板参数推导导致左侧T =浮动,右侧会出现错误,因为没有T,因此V< T>.等于S< T>.这有资格作为模板参数推导失败,简单地忽略模板.

如果您想允许转换,则您的操作符不应该是模板.有以下技巧:您可以将其定义为V的类模板中的内联朋友:

  1. template<class T>
  2. class V
  3. {
  4. public:
  5. V();
  6. V(S<T> const&); // <-- note: no explicit keyword here
  7.  
  8. friend V<T> operator+(V<T> const& lhs,V<T> const& rhs) {
  9. ...
  10. }
  11. };

这样,运算符就不再是模板了.因此,不需要模板参数推导,您的调用应该起作用.通过ADL(参数相关查找)找到运算符,因为左侧是V< float>.右侧适当地转换为V< float>同样.

也可以禁用特定参数的模板参数推断.例如:

  1. template<class T>
  2. struct id {typedef T type;};
  3.  
  4. template<class T>
  5. T clip(
  6. typename id<T>::type min,T value,typename id<T>::type max )
  7. {
  8. if (value<min) value=min;
  9. if (value>max) value=max;
  10. return value;
  11. }
  12.  
  13. int main() {
  14. double x = 3.14;
  15. double y = clip(1,x,3); // works,T=double
  16. }

即使第一个和最后一个参数的类型是int,但在模板参数推断期间不考虑它们,因为id< T> :: type不是所谓的* deducible context`.所以,T只是根据第二个参数推导出来,这导致T = double而没有矛盾.

猜你在找的C&C++相关文章