偏好转换运算符而非转换构造函数

时间:2011-06-20 17:19:03

标签: c++

我有以下代码段:

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 appears, compiler tries to use 
        //         template<class T> A(const T &rhs)
}

为什么编译器使用A的构造函数?如何才能将B的转换运算符用于A

我使用的是MSVS2010编译器。它给了我这些错误:

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 : 好吧,Nawaz所说的隐式转换确实有效。让它变得更复杂,如何使下面的代码工作?

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

UPD A是第三方库中的类,我无法编辑代码,因此我无法删除A的转换构造函数。

UPD :我目前发现的最简单的解决方案是为B定义转换构造函数的特化。它可以在第三方lib之外完成:

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

6 个答案:

答案 0 :(得分:9)

原因是只是因为它认为(A)bA(b)相同。标准说明了关于显式类型转换(5.4):

  

执行的转化      
      
  • const_cast(5.2.11),

  •   
  • static_cast(5.2.9),

  •   
  • static_cast后跟const_cast,

  •   
  • reinterpret_cast(5.2.10)或

  •   
  • reinterpret_cast后跟const_cast,

  •   
     

可以使用演员表演   显式类型转换的表示法。   相同的语义限制和   行为适用。

基本上这意味着即使是(A)b的显式类型转换(即如果你使用((A)b);来防止它是变量声明)。它使用static_cast的规则。现在让我们来看看标准对static_cast(5.2.9)所说的内容:

  

表达式e可以是显式的   使用a转换为类型T.   static_cast的形式   static_cast(e)如果声明   对于某些人来说,“T t(e);”格式正确   发明了临时变量t(8.5)。   这种明确的效果   转换与执行相同   声明和初始化和   然后使用临时变量作为   转换的结果。该   如果T是a,则结果是左值   引用类型(8.3.2)和右值   除此以外。表达式e用作   当且仅当一个左值   初始化将它用作左值。

如果你static_cast<A>(b),它基本上会看到A(b)是否格式正确;并且它是。仅仅因为模板函数copy-constructor的实际实例化失败,它不会使实际的声明形成错误,因此它使用它并最终失败。

答案 1 :(得分:5)

从5.4 / 1和5.4 / 5开始,C-cast从列表中选择“最佳选择”C ++。在这种情况下,这是一个static_cast

然后从5.2.9 / 2:

  

表达式e可以是显式的   使用a转换为类型T.   static_cast的形式   static_cast(e)如果声明   对于某些人来说,“T t(e);”形成得很好   发明了临时变量t(8.5)。   这种明确的效果   转换与执行相同   声明和初始化和   然后使用临时变量作为   转换的结果。该   如果T是a,则结果是左值   引用类型(8.3.2)和右值   除此以外。表达式e用作   当且仅当一个左值   初始化将它用作左值。

所以它在尝试任何其他选项之前选择构造函数。

在这种情况下,您已经定义了两次转换以获得相同的最终结果,但该语言具有特定的规则,规定它将始终使用可用的构造函数。您应该离开构造函数并将操作符更改为显式as - 类型函数。

编辑OP的编辑: 我认为你不能使用向量iter, iter构造函数。您需要从空向量开始,并使用for push_back的for循环或使用std::transform

答案 2 :(得分:3)

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

这是显式投射到A。因此,A的构造函数被调用以将b转换为A

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

演示:http://www.ideone.com/K9IxT

答案 3 :(得分:2)

如上所述,如果在格式良好的构造函数和转换运算符之间进行选择,编译器将始终调用构造函数。但是,如果您愿意,可以直接调用转换运算符:

A a = b.operator A();

答案 4 :(得分:1)

关于如何使其工作的问题的第二部分,我将使用std::transform而不是在第三方库名称空间中修改/添加内容,除非只有这个库已被充分记录并且它是打算你应该专门化这个构造函数:

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 ) );

答案 5 :(得分:-2)

语法(A)bA(b)相同,这是一种结构。如果您想要演员表,那么您必须明确使用static_cast,例如。