C++ 隐式转换没有发生
我问的最后一个问题是当我试图理解另一件事时偶然发现的。。。我也不明白(不是我的日子) 这是一个相当长的问题陈述,但至少我希望这个问题可能对很多人有用,而不仅仅是我 我的代码如下:C++ 隐式转换没有发生,c++,templates,implicit-conversion,C++,Templates,Implicit Conversion,我问的最后一个问题是当我试图理解另一件事时偶然发现的。。。我也不明白(不是我的日子) 这是一个相当长的问题陈述,但至少我希望这个问题可能对很多人有用,而不仅仅是我 我的代码如下: template <typename T> class V; template <typename T> class S; template <typename T> class V { public: T x; explicit V(const T & _x) :
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;
}
模板类别V;
模板类S;
模板
第五类
{
公众:
tx;
显式V(常数T和x)
:x(x){}
五级(施工安全)
:x(s.x){}
};
模板
S类
{
公众:
T&x;
显式S(V&V)
:x(v.x)
{}
};
模板
V操作员+(常数V&a、常数V&b)
{
返回V(a.x+b.x);
}
int main()
{
va(1);
vb(2);
sc(b),;
b=a+V(c);//1--编译
b=a+c;//2--失败
b=c;//3--编译
返回0;
}
表达式1和3工作正常,而表达式2无法编译
如果我理解正确,会发生以下情况:
表达式1
const
V(const S&S)
,并生成const V
对象的时态(我们称之为t)。它已经是const限定值,因为它是一个时间值operator+(const V&a,const V&b)
被调用,导致一个类型为const V
的时态,我们可以称之为qV::operator=(const&V)
V
。为此,我们有一个用户定义的转换序列:1.1. 第一次标准转换:
S
通过限定转换为const S
。1.2. 用户定义的转换:
const S
到V
通过V(const S&S)
构造函数。1.3第二次标准转换:通过资格转换将
V
转换为const V
V::operator=(const&V)
V
。为此,我们有一个用户定义的转换序列:1.1. 初始标准转换:
S
到const S
通过资格转换。1.2. 用户定义的转换:
const S
到V
通过V(const S&S)
构造函数。1.3. 最终标准转换:
V
通过鉴定转换为const V
李>
阅读C++标准后,我说:“嘿!也许问题出在13.3.3.1.2.3其中指出:
如果用户定义的转换由模板转换函数指定,则第二个标准转换序列必须具有精确的匹配秩 但事实并非如此,因为资格转换具有确切的匹配等级 我真的不知道好的,不管你有没有答案,谢谢你阅读到这里:)只是一个猜测,但是在试图找出如何在表达式2中添加a+c时,编译器可能无法区分从V->S到S->V的转换。您假设编译器足够聪明,可以选择一个允许编译继续进行的编译器,因为其余的函数都可用,但编译器可能没有“提前读取”(可以这么说),并且在试图找到“+”运算符之前,对上转换的模糊性感到困惑
当然,如果您添加了编译错误,也可能有助于澄清问题…在考虑模板匹配时,不使用隐式转换。因此,在以下简单示例中:
template < typename T >
void foo( T t1, T t2 ) { /* do stuff */ }
int main( int argc, char ** argv ) {
foo( 1, 1.0 );
return 0;
}
模板
voidfoo(tt1,tt2){/*dostuff*/}
int main(int argc,字符**argv){
foo(1,1.0);
返回0;
}
即使其中一个参数可以隐式转换为另一种类型(int-double),它也不会编译。正如Edric指出的,在模板参数推导过程中不考虑转换。在这里,您有两个上下文,其中模板参数T可以从参数的类型推断出来:
template<class T>
v<T> operator+(V<T> const&, V<T> const&);
~~~~~~~~~~~ ~~~~~~~~~~~~
模板
v运算符+(v常数&,v常数&);
~~~~~~~~~~~ ~~~~~~~~~~~~
但是,您尝试使用左侧的V
和右侧的S来调用此函数模板。模板参数推断的结果是左手边的T=float,而右手边则会出现错误,因为没有T,所以V
等于S
。这符合模板参数推断失败的条件,模板将被忽略
如果要允许转换,运算符+不应是模板。有以下技巧:您可以将其定义为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) {
...
}
};
模板
第五类
{
公众:
V();
V(S const&);//根据标准,运算符+是一个候选函数,因为它具有正确数量的参数。因此编译器应该能够跟踪我刚才编写的转换…或者我理解的那样。至于错误消息:../main.cpp:42:错误:与“a+c”中的“operator+”不匹配(抱歉,我不知道如何在这里添加新行)正如我所说的,这需要编译器“预读”并使用V构造函数转换c,然后它才能匹配指定的自定义运算符+
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
}