Question

Conversion Constructor vs Conversion Operator

Asked by: mkandre

Is there a precedence order that is used by compilers or something of that sort in determining whether to use a conversion constructor as opposed to a conversion operator where both are available for a particular operation?

In my situation, I have two classes (say First and Second) and class first has a conversion constructor for converting class Second instances, while class Second has a conversion constructor for converting to class First type.

In essence I want the compiler to use the conversion constructor for implicit conversions but use the conversion operator for explicit conversions (i.e. casts) because although they produce the same thing in the end the processing is different. However, It keeps using (or try to use) the conversion operator by default and ignoring the conversion constructor.

I have tried making the conversion constructor private and creating a third class as a friend and using that third class to execute the conversion operator via a macro (instead of explicit casting) but the compiler generates an error saying the conversion constructor is private.

How can I solve this problem?

P.S. I'm using MS Visual C++

class Second; // forward declare
 
class First
{
   public:
      First();
      First(const Second&);
      // other constructors and member functions
 
   private:
      //class data
};
 
 
class Second
{
   public:
      Second();
      // other constructors and member functions
      operator First() const;
 
   private:
      //class data
};
 
 
void myFunction(First obj)
{
   //do something
}
 
 
int main()
{
   Second item;
 
   myFunction(item);
 
   return 0;
}
                                  
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:

Select allOpen in new window

This Question has been solved and asker verified All Experts Exchange premium technology solutions are available to subscription members.

Subscribe now for full access to Experts Exchange and get

Instant Access to this Solution

  • Plus...
  • 30 Day FREE access, no risk, no obligation
  • Collaborate with the world's top tech experts
  • Unlimited access to our exclusive solution database
  • Never be left without tech help again

Subscribe Now

Asked On
2009-08-21 at 09:26:47ID24671879
Tags

Conversion

,

Operator

,

Constructor

,

Visual C++

Topics

C++ Programming Language

,

Microsoft Visual C++.Net

Participating Experts
4
Points
200
Comments
25

Trusted by hundreds of thousands everyday for fast, accurate and reliable tech support.

  • "The time we save is the biggest benefit of Experts Exchange to Warner Bros. What could take multiple guys 2 hours or more each to find is accessed in around 15 minutes on Experts Exchange." Mike Kapnisakis, Warner Bros.
  • "Our team likes having a resource that is more secure than just using Google and most experts using this service really know their stuff. It's nice to look here first versus using Google." Dayna Sellner, Lockheed Martin
  • "Anytime that I've been stumped with a problem, 9 out of 10 times Experts Exchange has either the accepted solution or an open discussion of the potential solution to the problem." Kenny Red, eBay Inc.

See what Experts Exchange can do for you.

Got a question?

We've got the answer.

Experts Exchange has been collecting answers to technology questions since 1996…3 million and counting! If you have a question, chances are we already have your answer.

Screenshot of Experts Exchange Knowledgebase

Need individual assistance?

Our experts are ready to help.

If you can't find the exact answer you're looking for, ask our exclusive community of 50,000 experts. You’ll get a personalized answer from a trusted professional.

Screenshot of Experts Exchange Knowledgebase

Want to learn from the best?

Read articles from industry experts.

Thousands of free tech tips, tricks, how-to’s and tutorials are available in our peer reviewed articles section. See for yourself how smart our experts are, no login required.

Screenshot of an Article

Working on a long term project?

Store your work and research.

Save solutions to your questions, answers you’ve discovered through searching plus helpful articles in your personal knowledgebase for easy future access.

Screenshot of Experts Exchange Knowledgebase

Access the answers to your technology questions today.

Subscribe Now

30-day free trial. Register in 60 seconds.

What Makes Experts Exchange Unique?

Members of the expert community talk about why the experience at Experts Exchange is different than what you will find anywhere else.

Trusted by the world's most respected brands.

image of each brand's logo

Faithfully serving IT professionals since 1996.

Experts Exchange Logo

Try it out and discover for yourself.

Subscribe Now

30-day free trial. Register in 60 seconds.

Related Solutions

  1. constructor
    if constructor declar in private part
  2. constructors
    What is the use of having a constructor in a private section.
  3. Copy constructor
    1) Why do we use copy constructor?how is it different from assignment operator?when does copy constructors called and when the assignment operators get called? 2) when & why do we declare the destructor as virtual? and why not virtual constructor?
  4. copy constructor
    under what all senario's copy constructor is invoked.hurry up guys

Free Tech Articles

  1. WARNING: 5 Reasons why you should NEVER fix a computer for free.
    It is in our nature to love the puzzle. We are obsessed. The lot of us. We love puzzles. We love the challenge. We thrive on finding the answer. We hate disarray. It bothers us deep in our soul. W...
  2. SCCM OSD Basic troubleshooting
    SCCM 2007 OSD is a fantastic way to deploy operating systems, however, like most things SCCM issues can sometimes be difficult to resolve due to the sheer volume of logs to sift through and the dispe...
  3. Migrate Small Business Server 2003 to Exchange 2010 and Windows 2008 R2
    This guide is intended to provide step by step instructions on how to migrate from Small Business Server 2003 to Windows 2008 R2 with Exchange 2010. For this migration to work you will need the fo...
  4. Create a Win7 Gadget
    This article shows you how to create a simple "Gadget" -- a sort of mini-application supported by Windows 7 and Vista. Gadgets can be dropped anywhere on the desktop to provide instant information, ...
  5. Outlook continually prompting for username and password
    There have been a lot of questions recently regarding Outlook prompting for a username and password whilst using Exchange 2007. There are a few reasons why this would happen and I will try to cover t...
  6. Backup Exchange 2010 Information Store using Windows Backup
    There seems to be quite a lot of confusion around the ability to backup Exchange 2010 using the built in Windows Backup feature. This stems from the omission of this feature prior to Exchange 2007 s...

Cloud Class Webinars

  1. Avoiding Bugs in Microsoft Access
    Alison Balter takes and in-depth look at avoiding bugs in Access. In this webinar you will learn about using the immediate window to debug your applications, invoking the debugger, using breakpoints to troubleshoot, stepping through code, setting the next statement to execute, ...
  2. Top 10 Best New Features in Visio 2010
    Scott Helmers gives live demonstrations of the top 10 new features in Visio 2010. This webinar will teach you how to create compelling diagrams by adding shapes to the page with a single click, linking the shapes in a diagram to data in Excel (or SQL Server, or SharePoint), ...
  3. IT Consultant Business Secrets Revealed
    Michael Munger, Experts Exchange tech pro and IT consultant, pulls back the curtain on his very successful businesses and answers question on every IT consultant and business owner should know about. He shares secrets on what he did to solve the 5 most common problems in IT, ...
  4. Disaster Recovery and Business Continuity
    Quest CTO, Mike Billon, gives an overview of the steps involved in building a dunamic disaster recovery plan. Through case studies and an examination of software/hardware tooles for monitoring and testing, you'll gain a better understandin of where you are, where you want ...
  5. Organize Your Visio Diagrams with Containers and Lists
    Scott Helmers uses cross functional flowcharts, wireframe diagrams, data graphic legends and seating charts to teach you: how to ustilize all three new structured diagram components in Visio 2010, the best practices for organizeing shapes in previous version of Visio, how to organize ...
  6. How to Us Objects, Properties, Events and Methods in Microsoft Access
    Alison Dalter gives an in-depbth look at objects, properties, events and methods in Microsoft Access. In this webinar you will learn about using the object browser, referring to objects, working with properties and methods, working with object variables, understanding the ...

Join the Community

Give a Little. Get a Lot.

Join the community of experts here and help other tech pros by answering question in your area of expertise. You can earn FREE access to all Experts Exchange's premium features and resources.

Join the Community

Answers

 

by: jkrPosted on 2009-08-21 at 09:35:03ID: 25153485

Maybe this one can help: http://msdn.microsoft.com/en-us/magazine/cc163742.aspx ("Copy Constructors, Assignment Operators, and More")

 

by: evilrixPosted on 2009-08-21 at 09:35:48ID: 25153492

The cast operator is a known cause of issues in C++ as it can have unexpected side effects and come into play when you don't want or expect it to (as you've discovered). Generally it is best to avoid using it. This is why the standard STL string has a c_str() member rather than a operator const char *(). I'd strongly recommend you remove the cast operator and use an explicit function call instead.

 

by: evilrixPosted on 2009-08-21 at 09:37:42ID: 25153509

More on why implicit cast operators are a bad idea.
http://www.ubookcase.com/book/Addison.Wesley/CPP.Coding.Standards.101.Rules.Guidelines.and.Best.Practices/0321113586/ch40.html

Example 1: Overloading. Say you have a Widget::Widget( unsigned int ) that can be invoked implicitly, and a Display function overloaded for Widgets and doubles. Consider the following overload resolution surprise:
 
void Display( double );             // displays a double
void Display( const Widget& );      // displays a Widget
 
Display( 5 );                       // oops: creates and displays a Widget
 
 
 
Example 2: Errors that work. Say you provide operator const char* for a String class:
 
class String {
  // &
public:
  operator const char*();          // deplorable form
};
 
 
 
Suddenly, a lot of silly expressions now compile. Assume s1, s2 are Strings:
 
int x = s1 - s2;                // compiles; undefined behavior
const char* p = s1 - 5;         // compiles; undefined behavior
p = s1 + '0';                   // compiles; doesn't do what you'd expect
if( s1 == "0" ) { ...}          // compiles; doesn't do what you'd expect
 
 
 
The standard string wisely avoids an operator const char* for exactly this reason.

 

by: Infinity08Posted on 2009-08-21 at 11:11:48ID: 25154315

And to answer your question :

>> Is there a precedence order that is used by compilers or something of that sort in determining whether to use a conversion constructor as opposed to a conversion operator where both are available for a particular operation?

Yes there is. It's described in paragraph 13.3.3 ("Best Viable Function") of the C++ standard.

However, in your example, it's ambiguous since both alternatives (the converting constructor and the cast operator) are equally viable, since both convert from a 'const Second' to a 'First'.

Strictly conforming compilers should tell you that the code is ill-formed, and generate an error message. Many compilers though will silently pick one, and continue compilation, unless you put the compiler in strict conformance mode (-pedantic for g++ for example).

 

by: mkandrePosted on 2009-08-21 at 12:09:43ID: 25154848

@evilrix
>> I'd strongly recommend you remove the cast operator and use an explicit function call instead.

In my actual case I have in essence removed the the conversion operator by making it private and use and external global friend function to explicitly call it when needed.

The problem, however, is that I would expect the compiler to default to the conversion constructor since the conversion operator is now private. But the compiler is telling me that the conversion operator function is private an generating an error instead.

class Second; // forward declare
 
class First
{
   public:
      First();
      First(const Second&);
      // other constructors and member functions
 
   private:
      //class data
};
 
 
class Second
{
   public:
      Second();
      // other constructors and member functions
 
   private:
      //class data
      operator First() const;
 
   template <class T, class U> friend U typeConvert(T);
};
 
 
void myFunction(First obj)
{
   //do something
}
 
 
template <class T, class U>
U typeConvert(T obj)
{
   return obj.operator U();
}
 
 
#define type_convert(TYPE, VAL) typeConvert<TYPE>(VAL)
 
int main()
{
   Second item;
   
   myFunction(item);  //should call conversion constructor
   
   type_convert(FIRST, item); //should call conversion operator
   
   return 0;
}
                                              
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:

Select allOpen in new window

 

by: Infinity08Posted on 2009-08-21 at 12:27:12ID: 25155007

whether a member function is private or not doesn't matter for overload resolution. And the same ambiguity I mentioned in my previous post exists here too.

The compiler doesn't know what you're trying to do, so it makes a guess, which is apparently the "wrong" one (ie. not the one you wanted). You need to be a bit more explicit/unambiguous when talking to the compiler ;)

 

by: mkandrePosted on 2009-08-21 at 14:00:58ID: 25155797

Ok. Understood.

So say I do replace the conversion operator with an regular member function, how do I overload it to get the appropriate return type where some return types are known and others aren't?

Would something like the attached code snippet work?

class Second; // forward declare
 
class First
{
   public:
      First();
      First(const Second&);
      // other constructors and member functions
 
   private:
      //class data
};
 
 
class Second
{
   public:
      Second();
      // other constructors and member functions
 
   private:
      //class data
      
      //conversion function
      template <class T>
      T convert() const;
 
   template <class T, class U> friend U typeConvert(T);
};
 
template <class T>
T Second::convert() const
{
   //error throw exception
}
 
template<>
First Second::convert<First>() const
{
   //convert and return
}
 
 
void myFunction(First obj)
{
   //do something
}
 
 
template <class T, class U>
U typeConvert(const T& obj)
{
   return obj.convert<U>();
}
 
 
#define type_convert(TYPE, VAL) typeConvert<TYPE>(VAL)
 
int main()
{
   Second item;
   
   myFunction(item);  //should call conversion constructor
   
   type_convert(FIRST, item); //should call conversion operator
   
   return 0;
}
                                              
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:

Select allOpen in new window

 

by: Infinity08Posted on 2009-08-21 at 14:11:19ID: 25155873

You can't overload functions on their return type, so no :)

You could however make the conversion functions take a parameter, and convert in the "other direction", as shown below.

It's cleaner this way, since you don't have to give the outside world access to the internals of the Bar class.

You probably also want to follow evilrix's advice not to use any converting constructors either (except maybe for explicit ones, but even then I wouldn't).

class Foo;
 
class Bar {
  public :
    void convert(const Foo&);   // fill the current Bar object by converting from the Foo object passed by reference
};

                                              
1:
2:
3:
4:
5:
6:

Select allOpen in new window

 

by: evilrixPosted on 2009-08-21 at 14:22:38ID: 25155955

>> The problem, however, is that I would expect the compiler to default to the conversion constructor since the conversion operator is now private. But the compiler is telling me that the conversion operator function is private an generating an error instead.

Like I said before, the cast operator works in ways that are both unintuitive and often surprising. In the end all it saves you is a little bit of typing yet it introduces ambiguities in your code that is often hard to spot or predict. It's your code and you are free to choose your own coding standards but the cast operator has widely accepted to be a good example of just because you can do something it doesn't mean you should.

That all said, the reason your code above doesn't build is because your template parameters were wrong. You were using T and U the wrong way around.

class Second; // forward declare
 
class First
{
   public:
      First(){}
      First(const Second&){}
      // other constructors and member functions
 
   private:
      //class data
};
 
 
class Second
{
   public:
      Second(){}
      // other constructors and member functions
 
   private:
      //class data
      operator First() const { return First(); }
 
   template <class T, class U> friend T typeConvert(U);
};
 
 
void myFunction(First obj)
{
   //do something
}
 
 
template <class T, class U>
T typeConvert(U obj)
{
   return obj.operator T();
}
 
 
#define type_convert(TYPE, VAL) typeConvert<TYPE>(VAL)
 
int main()
{
   Second item;
   
   myFunction(item);  //should call conversion constructor
   
   type_convert(First, item); //should call conversion operator
   
   return 0;
}
                                              
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:

Select allOpen in new window

 

by: Infinity08Posted on 2009-08-21 at 14:29:15ID: 25156000

>> That all said, the reason your code above doesn't build is because your template parameters were wrong. You were using T and U the wrong way around.

The ambiguity is still there though ;)

 

by: evilrixPosted on 2009-08-21 at 14:30:43ID: 25156016

>> The ambiguity is still there though ;)
I agree... I don't advocate this code be used... but the actual error as the code stands was related to the template definition being incorrect... I am with you 100% on this :)

 

by: Infinity08Posted on 2009-08-21 at 14:30:58ID: 25156018

>> The ambiguity is still there though ;)

Meaning that for a strictly conforming compiler, it will still not compile.

 

by: Infinity08Posted on 2009-08-21 at 14:31:40ID: 25156024

>> I am with you 100% on this :)

And I with you ;)

 

by: mkandrePosted on 2009-08-23 at 12:44:01ID: 31618942

Although my problem hasn't been completely resolved, the question as posed has been answered. As such I am obliged to mark the question as answered and award points. Well done.

 

by: Infinity08Posted on 2009-08-23 at 12:50:16ID: 25164002

>> Although my problem hasn't been completely resolved

We're still here to help you out with the part of your problem that hasn't been resolved yet. Just tell us what it is :)

 

by: itsmeandnobodyelsePosted on 2009-08-23 at 13:08:36ID: 25164047

>>>> I don't advocate this code be used...
Yes, it contradicts the first recommendation that cast operators shouldn't be used. Moreover, a template function should spare code by using the same functional body for all types. If a template function is depending on existing cast operators which provide individual code for each type, it isn't much more than shadowboxing.

You should provide member functions First::fromSecond(const Second & s) or a global function like secondToFirst(const Second & s, First & f) to get a clear and easy-to-understand interface rather than to rely on implicit conversions.

>>>> This is why the standard STL string has a c_str() member rather than a operator const char *().
Actually, this is the only sample where I miss the cast operator known from other string classes. As it is a cast to a const type (const char *) possible side effects would be small and IMO could be neglected.

 

by: evilrixPosted on 2009-08-23 at 13:16:03ID: 25164082

>> As it is a cast to a const type (const char *) possible side effects would be small and IMO could be neglected.
It has nothing to do with constness. Look at the following examples by Alexandrescu...

int x = s1 - s2;                // compiles; undefined behavior
const char* p = s1 - 5;         // compiles; undefined behavior
p = s1 + '0';                   // compiles; doesn't do what you'd expect
if( s1 == "0" ) { ...}          // compiles; doesn't do what you'd expect

...none of these are as a result of constness or otherwise.
 

 

by: mkandrePosted on 2009-08-23 at 15:15:26ID: 25164491

I replaced the cast operator with a convert function and it works generally as code in the code snippet. However, I have to make the convert function public and comment out the global typeConvert function.

Whenever I compile using the global typeConvert function I get a compiler error:

      error C2440: 'initializing' : cannot convert from 'Second' to 'First'

what am I doing wrong?


class Second; // forward declare
 
class First
{
   public:
      First();
      First(const Second&);
      // other constructors and member functions
 
   private:
      //class data
};
 
 
class Second
{
   public:
      Second();
      // other constructors and member functions
 
   private:
      //class data
      
      //conversion function
      template <class T>
      T convert() const;
 
   template <class T, class U> friend U typeConvert(T);
};
 
template <class T>
T Second::convert() const
{
   //error throw exception
}
 
template<>
First Second::convert<First>() const
{
   //convert and return
}
 
 
void myFunction(First obj)
{
   //do something
}
 
 
template <class T, class U>
U typeConvert(const T& obj)
{
   return obj.convert<U>();
}
 
 
#define type_convert(TYPE, VAL) typeConvert<TYPE>(VAL)
 
int main()
{
   Second item;
   
   myFunction(item);  //should call conversion constructor
   
   type_convert(FIRST, item); //should call conversion operator
   
   return 0;
}
                                              
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:

Select allOpen in new window

 

by: mkandrePosted on 2009-08-23 at 15:20:17ID: 25164507

correction: i was trying to transpose the the error message didn't do too well. It should be more like cant convert from 'Second' to 'Second*' or 'Second&'

 

by: Infinity08Posted on 2009-08-23 at 22:46:10ID: 25165816

It seems to me you're making things way more complicated than they need to be.

How about just implementing the convert function I showed in http:#25155873, and call that whenever you need to convert from Foo to Bar ? And get rid of all converting constructors and cast operators.

 

by: mkandrePosted on 2009-08-24 at 01:30:05ID: 25166415

Ok I get what you saying...thanks

 

by: itsmeandnobodyelsePosted on 2009-08-24 at 16:03:07ID: 25173331

>>>> Look at the following examples by Alexandrescu...

Bad samples. If you have a cast operator turning an object to a pointer, it necessarily is the chance that pointer  arithmethics go wrong, but if you do such kind of programming things go wrong anyway ...

The last sample definitively is false for CString class and my own various string classes which all had a cast operator for const char* but also an operator== which take a const char * as right operand and a global operator which takes a const char* as left operand and a string class object as right operand. Given those operators the compilers I used never made a cast but use the correct operator==.

Same applies for the third sample. If an operator+ exists which takes a right hand char operand, the right term correctly would use the operator+.  The flaw is that such a temporary shouldn't be assigned to a pointer.  But those bads can be done without cast operators as well:

  string str = "Hello";
  const char * p = (str + 'a').c_str();

>>>> ...none of these are as a result of constness or otherwise

Yes because they were bad samples.

A really pitfall could (only) occur if you provide a cast to a non-const pointer as it may destroy the internal buffer or writes out of boundaries.

 

by: itsmeandnobodyelsePosted on 2009-08-24 at 16:07:15ID: 25173360

>>>> How about just implementing the convert function I showed in http:#25155873, and call that whenever you need to convert from Foo to Bar ? And get rid of all converting constructors and cast operators.
>>>> Ok I get what you saying...thanks

If so, you did choose the wrong answer of Infinity as an assist.

 

by: evilrixPosted on 2009-08-24 at 23:42:37ID: 25174985

Ok Alex, you clearly know more than one of the words leading experts on C++.
http://en.wikipedia.org/wiki/Andrei_Alexandrescu

 

by: itsmeandnobodyelsePosted on 2009-08-25 at 02:16:10ID: 25175671

>>>> know more than one of the words leading experts on C++.
Bad samples are bad samples. Wether they were from a guru or not.

The drawbacks of pointer arithmetics are one thing, providing a cast operator to const char * for a string class is another thing. The latter is useful as various interfaces of runtime functions and standard class member functions take a const char * but not a string class member, e. g.

       const string & filename = "abc.txt";
       ifstream ifs(filename);    // error: cannot take std::basic_string as argument

The cstr() function is a way out but it is neither intuitive nor generalized as other striing classes may have a cast operator or a function with different name.

When I compare the benefits to the drawbacks it is big benefit and a small drawback. Other experts may have a different view on that but their arguments don't have convinced me so far.

20120131-EE-VQP-002

3 Ways to Join

30-Day Free Trial

The Experts

98% positive feedback on 31,087 answers since March 2000. angeliii is a Microsoft Most Valuable Professional for his work with MS SQL Server & Develoment.

He has also proven his knowledge of Visual Basic Programming, PHP Scripting and Oracle Databases.

The Experts

97% positive feedback on 10,752 answers since July 2000. lrmoore has more than 18 years experience in the networking industry.

The six-time Mircosoft MVPs specialties include firewalls, virtual private networking, and network management.

Testimonials

"...and excellent source for support... Kind of like having your very own IT dept." Electriciansnet

Testimonials

"I was apprehensive at signing up at first. However... it has already made my life as an IT administrator much easier." JaCrews

Testimonials

"WOW! You guys have great, active, and knowledgeable people on here." moore50

Business Clients

Business Clients

In the Press

"If you’ve got a question... Experts Exchange can supply an answer.”

In the Press

"...an invaluable aid for both IT professionals and those who require tech support."

In the Press

"where IT professionals provide quick answers on just about any topic"

Business Account Plans

Loading Advertisement...