为什么以下两个用例表现不同?
内置类型的示例:
class Test
{
operator const char*() {
return "operator const char*() called";
}
operator char*() {
return const_cast<char*> ("operator char*() called");
// a really ugly hack for demonstration purposes
}
};
Test test;
const char *c_ch = test; // -> operator const char*() called
char *ch = test; // -> operator char*() called
好的,一切正常。不,让我们尝试使用用户定义的类型:
class ColorWrapper
{
operator const Color() {
cout << "operator const Color() called" << endl;
return Color(10, 20, 30);
}
operator Color() {
cout << "operator Color() called" << endl;
return Color(11, 22, 33);
}
};
ColorWrapper cw;
const Color c_col = cw;
Color col = cw;
情况看起来相同;但现在,海湾合作委员会开始抱怨:
error: conversion from 'ColorWrapper' to 'const Color' is ambiguous
error: conversion from 'ColorWrapper' to 'Color' is ambiguous
我错过了什么吗?我在这里想要实现的是防止用户直接改变颜色。如果他们想要改变它,他们必须通过包装类来完成它。
答案 0 :(得分:3)
问题是,你的两个例子并不等同。第一个返回编译器可以轻松选择的两种不同类型,一个指向可变数据的指针和一个指向常量数据的指针。它会有所不同,它是指针与常量指针(这意味着你不能改变返回指针指向的位置,与不改变指向数据的对比) :
class Test
{
operator char * const();
operator char *();
};
这将等同于你的第二个例子并引入相同的歧义,因为编译器无法决定选择哪一个,因为它们都返回相同的类型,一个是const限定的而另一个不是。
这可以通过基于对象的常量重载来解决:
class ColorWrapper
{
operator const Color() const;
operator Color();
};
但是这里不会给你带任何东西,因为你总是按值返回一个新对象,所以你总是可以返回const版本,并且根据源对象的constness而不是目标来选择合适的版本。 obejct的constness(因此在两种情况下都调用非const版本),这似乎不是你想要的。
因此,您可以根据源对象的常量进行重载。但是,您现在不能仅根据目标对象的常量重载。
这一切都减少了思考对象和对象可能无法正确引用的数据之间的差异,这实际上表现为常量指针和指向常量数据的指针之间的差异。
我在这里想要实现的是防止用户改变 颜色直接
然后绝对不需要任何超载。只需返回const版本。用户无法直接更改返回的颜色,他只能复制并更改副本。
cw.set(red); //nope, return color is constant
Color col = cw; //copy returned color into col
col.set(green); //fine, since we're not working on the returned color, but on col
const Color c_col = cw; //use this if you don't want c_col to be modified,
//doesn't have anything to do with returned color, though
答案 1 :(得分:2)
那是因为在你的第二个例子中你是将const添加到一个对象而不是指针(在你的第一个例子中)。而const与对象在重载解析阶段使编译混淆,根据c ++标准,这两者彼此相同(对象与const对象)。编译器不会尝试智能在这里选择一个而不是另一个。
但是在指针vs const指针(引用也是如此)中,编译可以相应地选择那两个重载,这是你的第一种情况。
答案 2 :(得分:0)
如果您将一个运算符修改为const
,它将起作用:
operator const Color() const {
cout << "operator const Color() const called" << endl;
return Color(10, 20, 30);
}
或者,您可以删除两个运算符中的一个。由于您按值返回Color
,因此两个运算符之间没有真正的区别。
答案 3 :(得分:0)
你观察到这个问题,因为你这里基本上有两种不同的情况。我认为,提出这里发生的事情的最简单方法是修改你的第一个例子:
class Test
{
operator char* const() {
return "operator char* () called";
}
operator char*() {
return const_cast<char*> ("operator char*() called");
// a really ugly hack for demonstration purposes
}
};
现在您的情况与第二个示例完全相同。你基本上返回一个副本(一个指针)和一个const副本,这就是他们陷入重载决议的原因。
您原来的第一个示例运行正常,因为这些转换运算符会转换为2种不同类型的指针(const char
和char
)。我希望这有助于理解这里的差异。