c – 没有发生隐式转换

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

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

我的代码如下:

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.

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

我真的不知道……

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

解决方法

正如Edric所指出的那样,在模板参数推导期间不会考虑转换.在这里,您有两个上下文,其中模板参数T可以从参数的类型推导出来:
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而没有矛盾.

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