我想知道在c ++中是否有可能像在这个C#示例中那样实现相同的强制重载:
class A {
public static implicit operator A(string s) {
return new A();
}
public static implicit operator A(double d) {
return new A();
}
static void Main(string[] args) {
A a = "hello";
A b = 5.0;
}
}
在C ++中它应该是这样的:
#include <string>
using namespace std;
class A{
/*SOME CAST OVERLOADING GOES HERE*/
};
void main(){
A a = "hello";
A b = 5.0;
}
你能帮助我如何使这个演员超载吗?
答案 0 :(得分:16)
这通常通过构造函数实现:
class A
{
public:
A(const std::string & s) { /*...*/ }
A(double d) { /*...*/ }
//...
};
用法:A a("hello"), b(4.2), c = 3.5, d = std::string("world");
(如果声明构造函数explicit
,则只允许第一个表单(带有括号的“直接初始化”)。否则这两个表单完全相同。)
由于这个原因,单参数构造函数也称为“转换构造函数”:您可以使用它来构造来自其他对象的对象,即“转换”double
,例如,转换为{ {1}}。
广泛使用隐式转换。例如,它适用于以下情况:
A
这构造了临时值void f(A, const A&) { /* ... */ }
int main() { f(1.5, std::string("hello")); }
和A(1.5)
,并将它们传递给A("hello")
。 (Temporaries也会绑定到常量引用,正如您可以在第二个参数中看到的那样。)
更新:(致@cHao查询。)根据标准(12.3.4),“最多隐式应用一个用户定义的转换(构造函数或转换函数)一个单一的价值。“ (这指的是隐式转换; la f
的直接初始化不属于此*。请参阅this related question。)很遗憾,您不能说A a("hello");
。 (当然你可以添加一个f("hello", "world");
构造函数,在新的C ++ 11中你甚至可以毫不费力地转发给字符串构造函数。)
*)我认为这实际上有点微妙。只有用户定义的转换受规则影响。首先,您可以免费从const char *
到char (&)[6]
进行基本转换。然后,您将获得从const char *
到const char *
的隐式转换。最后,您从std::string
到std::string
进行显式转换构建。
答案 1 :(得分:2)
转换运算符将提供从您的类型到其他类型的隐式转换。例如:
class A
{
public:
operator std::string () const { return "foo"; }
};
但是,隐式转换can be dangerous是因为当您不希望它被使用并且更喜欢编译器错误时可以使用它。因此,在目标对象上实现转换构造函数通常更好,而不是在源对象上实现转换运算符。
class A
{
public:
A(const std::string& rhs) { /* ... */ }
};
然而,仍然存在一些隐式转换。考虑:
string f = "foo";
A foo = f;
f
被隐式转换为A
,因为转换构造函数可用。但是,这可能最好导致编译器错误。通过标记转换构造函数explicit
,您可以轻松地将其从一种类型转换为另一种类型,但只有 才能实现。
#include <string>
using namespace std;
class A
{
public:
A() {};
explicit A(const std::string& ) {};
};
int main()
{
string f = "foof";
A a1 = f; // WONT COMPILE
A a(f); // WILL COMPILE
}
答案 2 :(得分:1)
不需要进行转换......这可以通过为您的类创建构造函数来实现,这些构造函数采用正确的参数类型,而仅使用单个参数,因此它们可以通过隐式调用编译器。
如果构造函数采用多个非默认参数,则它们不能用于转换操作。
此外,如果您希望避免基于推断类型转换选择构造函数时出现歧义,则可以始终在构造函数中使用explicit
关键字。例如,想象一下这样的场景:
struct test
{
int a;
test (signed int b): a(b) {}
test (unsigned int b): a(b) {}
};
int main()
{
test A = 5.0;
return 0;
}
由于double
类型转换为缩放器类型的模糊性,这将导致编译器错误... double
类型可以隐式转换为两种类型。这可以通过使用explicit
关键字来解决,以执行以下操作:
struct test
{
int a;
test (signed int b): a(b) {}
explicit test (unsigned int b): a(b) {}
};
现在unsigned int
的{{1}}版本的构造函数只有在传递test
时才会被调用。来自unsigned int
类型的转化操作将使用默认的double
版本,从而消除歧义问题。
答案 3 :(得分:0)
您可以为构造函数提供一个所需类型的参数:
class Foo {
int bar;
public:
Foo(const int bar) : bar(bar) {};
}
Foo foo = 5;
答案 4 :(得分:0)
在C#中,语义不是重载operator =
(你不能),而是提供从任何类型到类型A或从类型A到任何类型的强制转换操作符。
这意味着以下代码(使用A到类型转换完成):
class A {
public static implicit operator A(string s) {
return new A();
}
public static implicit operator A(double d) {
return new A();
}
public static implicit operator string(A a) {
return string.Empty;
}
public static implicit operator double(A a) {
return 0.0;
}
static void Main(string[] args) {
A a = "hello"; // line A
A b = 5.0; // line B
a = "World"; // line C
a = 3.14; // line D
double d = a ; // line E
string s = a ; // line F
}
}
适用于赋值,可以是简单赋值(如C和D行),也可以是赋值(如A行和B行)。 E和F行演示了我们如何从用户类型转换为其他类型。
在C ++中,A行和B行是对象结构,它们将调用相关的构造函数。
C和D行是赋值,它将调用相关的赋值运算符。
因此,您提供的C#代码的C ++代码必须提供字符串和双精度的构造函数和赋值,如下所示:
class A
{
public:
A(const std::string & s) { /*...*/ }
A(double d) { /*...*/ }
A & operator = (const std::string & s) { /*...*/ ; return *this ; }
A & operator = (double d) { /*...*/ ; return *this ; }
// etc.
} ;
这样,你可以拥有
void main()
{
A a0 = "Hello" ; // constructor
A a1("Hello") ; // constructor
A a2 = 5.0 ; // constructor
A a3(5.0) ; // constructor
a0 = "Hello World" ; // assignment
a0 = 3.14 ; // assignment
}
C ++中的强制转换运算符与下面的C#转换运算符类似:
class A
{
static public operator string(A a) { /*... ; return a string */ }
static public operator double(A a) { /*... ; return a double */ }
// etc.
}
在C ++中,强制转换操作符的编写如下:
class A
{
public:
operator std::string() { /*... ; return a string */ }
operator double() { /*... ; return a double */ }
// etc.
} ;
void main()
{
A a ;
std::string s ;
double d ;
// etc.
s = a ; // cast operator
d = a ; // cast operator
}
在C#中,转换/转换运算符可以从类类型写入任何类型,或从任何类型写入类类型。
在C ++中,对于转换,您必须在一种或多种方式之间进行选择,即:构造函数,赋值运算符或强制转换运算符,因为在C ++中,您对类型和操作有一个细粒度的控制,因此,您必须使用细粒度的API。
构造意味着对象不存在,因此将它构造为某个默认值然后为其分配另一个值(这可能会降低速度)没有意义。在C#中,引用/值类型在赋值之前为空/归零,因此这不是问题。
赋值意味着对象已经存在,因此可以重用相同的内部来容纳新值。在C#中,原始引用对象被丢弃,另一个被创建(如果创建成本,您将支付该价格)
对于强制类型转换,它在当前情况下用于将现有类转换为您无法控制的另一个类:您无权扩展std :: string以适应您的类的构造函数,并且无法将构造函数添加到内置类型,如double。