3

让我们考虑以下代码:

class A{
public:
  A(int x){}
};

class B{
public:
  B(A a){};
};


int main() {
  B b = 5;
  return 0;
}

在编译编译器时抱怨:

/home/test/main.cpp:80: candidate constructor not viable: no known conversion from 'int' to 'A' for 1st argument

我不想创建B(int)构造函数 - 我希望编译器将其隐式转换intA对象。

编辑:

直接初始化的工作方式如下:

B b(5);

是否可以改用赋值运算符?

4

3 回答 3

3

只是要清楚:

B b = 5;

是“复制初始化”而不是分配。(见http://www.gotw.ca/gotw/036.htm)。

在这种情况下,您要求编译器首先执行两个隐式用户定义转换,即int -> A, A -> B在将临时B对象传递给B b. 允许编译器省略临时对象,但从语义上讲,您仍然要求语言在类型之间进行两次跳转。

编程语言中的所有隐式行为本质上都是可怕的。为了一点语法糖的缘故,我们要求 c++ “做一些魔法让它正常工作”。意外的类型转换可能会在大型复杂程序中造成严重破坏。否则,每次您编写新函数或新类时,您都必须担心它可能影响的所有其他类型和函数,而副作用会波及您的代码。你真的想要来自int-> apple-> horse-> horse_power->的隐式转换aeroplane吗?

出于这个原因,c++ 只允许一个隐式的用户定义转换:

12.3 转换 [class.conv]

1 类对象的类型转换可以由构造函数和转换函数指定。这些转换称为用户定义的转换,用于隐式类型转换(第 4 条)、初始化(8.5)和显式类型转换(5.4、5.2.9)。

4 最多一个用户定义的转换(构造函数或转换函数)隐式应用于单个值。

您最好使用显式强制转换或“直接初始化”,这两者都可以让编译器和协作者清楚地知道您正在尝试做什么。传统的或新的统一初始化语法都有效:

B b(5);
B b{5};
B b = {5};
于 2013-06-27T16:43:50.937 回答
0

改用直接初始化:

B b(5);
于 2013-06-27T09:50:22.860 回答
0

您可以使用一个转换构造函数,该构造函数转换为A.

class B {
public:
    // It doesn't hurt to keep that one
    B(A a){};

    template<
        typename T
        , EnableIf<std::is_convertible<T, A>>...
    >
    B(T&& value)
    {
        // How to use the value
        A a = std::forward<T>(value);
    }
};

// Now B b = foo; is valid iff A a = foo; is, except for a few exceptions

确保您了解EnableIf约束的目的。如果您不使用它,您将面临严重的编译错误,或者更糟糕的是:根本没有错误。您还应该仔细考虑是否B值得从潜在的许多类型中进行转换。隐式转换往往会使程序更难理解,这很快就会超过它们带来的明显好处。

于 2013-06-27T11:09:55.477 回答