jme*_*paz 7 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)
: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;
}
Run Code Online (Sandbox Code Playgroud)
表达式1和3完美地工作,而表达式2不能编译.
如果我理解得当,会发生什么:
表达1
const通过使用标准转换序列(仅包含一个限定转换)隐式转换为.V<float>(const S<T> & s)被调用和const V<float>生成对象的时间(让我们称之为t).它已经是const限定的,因为它是一个时间值.operator+(const V<float> & a, const V<float> & b)被调用,导致const V<float>我们可以称为q的类型的时间.V<float>::operator=(const & V<float>)调用默认值.我可以到这儿了吗?如果我做出了最微妙的错误,请告诉我,因为我试图尽可能深入了解隐式铸造......
表达3
V<float>.为此,我们有一个用户定义的转换序列:S<float>对const S<float>通过资格转换.const S<float>到V<float>经由V<float>(const S<T> & s)构造.V<float>对const V<float>通过资格转换.V<float>::operator=(const & V<float>)调用默认值.表达2?
我不明白的是为什么第二个表达式存在问题.为什么以下顺序不可能?
V<float>.为此,我们有一个用户定义的转换序列:S<float>对const S<float>通过资格转换.const S<float>到V<float>经由V<float>(const S<T> & s)构造.V<float>对const V<float>通过资格转换. 在阅读了C++标准之后我说:'嘿!或许问题必须与13.3.3.1.2.3!' 其中说明:
如果用户定义的转换由模板转换函数指定,则第二个标准转换序列必须具有完全匹配等级.
但情况并非如此,因为资格转换具有完全匹配等级......
我真的不知道......
好吧,不管你有没有答案,谢谢你在这里阅读:)
sel*_*tze 12
正如Edric所指出的那样,在模板参数推导期间不会考虑转换.在这里,您有两个上下文,其中模板参数T可以从参数的类型推导出来:
template<class T>
v<T> operator+(V<T> const&, V<T> const&);
~~~~~~~~~~~ ~~~~~~~~~~~~
Run Code Online (Sandbox Code Playgroud)
但是您尝试调用此函数模板,V<float>左侧为a ,右侧为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) {
...
}
};
Run Code Online (Sandbox Code Playgroud)
这样,操作员就不再是模板了.因此,不需要模板参数推导,您的调用应该起作用.通过ADL(参数依赖查找)找到运算符,因为左侧是a V<float>.右侧也正确转换为a 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
}
Run Code Online (Sandbox Code Playgroud)
即使第一个和最后一个参数的类型是int,但在模板参数推导期间不会考虑它们,因为id<T>::type它不是所谓的*deducible context`.所以,T只是根据第二个参数推导出来,这导致T = double而没有矛盾.
当考虑模板匹配时,不使用隐式转换。因此,在下面的简单示例中:
template < typename T >
void foo( T t1, T t2 ) { /* do stuff */ }
int main( int argc, char ** argv ) {
foo( 1, 1.0 );
return 0;
}
Run Code Online (Sandbox Code Playgroud)
即使任一参数可以隐式转换为另一种类型(int <-> double),也不会编译。
| 归档时间: |
|
| 查看次数: |
1292 次 |
| 最近记录: |