开发者

prefer conversion operator over conversion constructor

I have the following code snippet:

class A
{
public:
  A() : x_(0), y_(0) {}

  A(int x, int y) : x_(x), y_(y) {}

  template<class T>
  A(const T &rhs) : x_(rhs.x_), y_(rhs.y_)
  {
  }

  int x_, y_;
};

class B
{
public:
  B() {}

  operator A() const { return A(c[0],c[1]); }
  int c[2];
};

void f()
{
  B b;
  (A)b; // << here the error开发者_C百科 appears, compiler tries to use 
        //         template<class T> A(const T &rhs)
}

Why compiler uses A's constructor? How can I make it use B's conversion operator to A?

I use MSVS2010 compiler. It gives me these errors:

main.cpp(9): error C2039: 'x_' : is not a member of 'B'
          main.cpp(17) : see declaration of 'B'
          main.cpp(28) : see reference to function template instantiation 'A::A<B>(const T &)' being compiled
          with
          [
              T=B
          ]
main.cpp(9): error C2039: 'y_' : is not a member of 'B'
          main.cpp(17) : see declaration of 'B'

UPD: All right, implicit convert as Nawaz said really works. Let's make it more complicated, how make the following code work?

void f()
{
  std::vector<B> b_vector(4);
  std::vector<A> a_vector( b_vector.begin(), b_vector.end() );
}

UPD: A is the class in 3rd party lib which code I can't edit, so I can't remove A's converting constructor.

UPD: the simplest solution I've found for the moment is to define specialization of converting constructor for B. It can be done outside of 3rd party lib:

template<> A::A( const B &rhs ) : x_(rhs.c[0]), y_(rhs.c[1]) {}


The reason is not only because it thinks (A)b is same as A(b). The standard says this about explicit type-conversion (5.4):

The conversions performed by

  • a const_cast (5.2.11),

  • a static_cast (5.2.9),

  • a static_cast followed by a const_cast,

  • a reinterpret_cast (5.2.10), or

  • a reinterpret_cast followed by a const_cast,

can be performed using the cast notation of explicit type conversion. The same semantic restrictions and behaviors apply.

Essentially this means that even for explicit type-conversion of (A)b (i.e. if you used ((A)b); to prevent from it being a variable declaration). it'd use the rules of static_cast. Now let's take a look at what the standard says about static_cast (5.2.9):

An expression e can be explicitly converted to a type T using a static_cast of the form static_cast(e) if the declaration “T t(e);” is well-formed, for some invented temporary variable t (8.5). The effect of such an explicit conversion is the same as performing the declaration and initialization and then using the temporary variable as the result of the conversion. The result is an lvalue if T is a reference type (8.3.2), and an rvalue otherwise. The expression e is used as an lvalue if and only if the initialization uses it as an lvalue.

If you do static_cast<A>(b), it basically sees if A(b) is well-formed; and it is. Just because the actual instantiation of the template function copy-constructor fails, it doesn't make the actual declaration is ill-formed, hence it uses it and ultimately fails.


From 5.4/1 and 5.4/5 the C-cast picks the "best choice" C++ cast from a list. In this case, that's a static_cast.

Then from 5.2.9/2:

An expression e can be explicitly converted to a type T using a static_cast of the form static_cast(e) if the declaration “T t(e);” is well formed, for some invented temporary variable t (8.5). The effect of such an explicit conversion is the same as performing the declaration and initialization and then using the temporary variable as the result of the conversion. The result is an lvalue if T is a reference type (8.3.2), and an rvalue otherwise. The expression e is used as an lvalue if and only if the initialization uses it as an lvalue.

So it picks the constructor before even attempting any other option.

In this case you've defined two conversions to get to the same end result, but the language has specific rules dictating that it will always use the available constructor. You should probably leave the constructor and change the operator to an explicit as-type function instead.

EDIT for OP's edit: I don't think you'll be able to use the vector iter, iter constructor. You'll need to start with an empty vector and either use a for loop with push_back or use std::transform.


(A)b; // << here the error appears, compiler tries to use 

This is explicit casting to A. Hence the constructor of A gets invoked to convert b to A.

A a(1,2);
a = b ; //this will invoke user-defined conversion of B (implicit conversion)

Demo : http://www.ideone.com/K9IxT


As mentioned, given the choice between a well-formed constructor and a conversion operator, the compiler will always call the constructor. But you could call the conversion operator directly, if you wish:

A a = b.operator A();


As for the second part of your question on how to make it working, I would use std::transform instead of modifying/adding something in the 3rd-party library namespace, unless only this library is well documented and it is intended that you should specialize this constructor:

a_vector.reserve( b_vector.size() );
std::transform( b_vector.begin(), b_vector.end(), 
  std::back_inserter( a_vector ), boost::bind( &B::operator A, _1 ) );


The syntax (A)b is the same as A(b), which is a construction. If you want a cast, then you must explicitly use static_cast, for example.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜