c ++ cast overload等价物

时间:2011-11-15 15:32:07

标签: c# c++ casting

我想知道在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;
}

你能帮助我如何使这个演员超载吗?

5 个答案:

答案 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::stringstd::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#中意味着什么?

在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 ++中完成?

在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 ++中的强制转换运算符与下面的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 ++中,对于转换,您必须在一种或多种方式之间进行选择,即:构造函数,赋值运算符或强制转换运算符,因为在C ++中,您对类型和操作有一个细粒度的控制,因此,您必须使用细粒度的API。

构造意味着对象不存在,因此将它构造为某个默认值然后为其分配另一个值(这可能会降低速度)没有意义。在C#中,引用/值类型在赋值之前为空/归零,因此这不是问题。

赋值意味着对象已经存在,因此可以重用相同的内部来容纳新值。在C#中,原始引用对象被丢弃,另一个被创建(如果创建成本,您将支付该价格)

对于强制类型转换,它在当前情况下用于将现有类转换为您无法控制的另一个类:您无权扩展std :: string以适应您的类的构造函数,并且无法将构造函数添加到内置类型,如double。