编译器错误 C2440

“initializing”:无法从“type1”转换为“type2”
“conversion”:无法从“type1”转换为“type2”

编译器无法从 *type1* 隐式转换为 *type2*,也不能使用指定的强制转换或转换运算符。

注解

当编译器无法从一种类型转换为另一种类型时,编译器会生成 C2440,无论是隐式转换还是使用指定的强制转换或转换运算符。 有多种情况会生成此错误。 我们已在“示例”部分列出了一些常见项。

示例

C++ 字符串文本是 const

如果在设置编译器一致性选项 /Zc:strictStrings 时尝试通过在 C++ 代码中使用字符串文本来初始化非 constchar*(或 wchar_t*),则可能会导致 C2440。 在 C 中,字符串文本的类型是 char 数组,但在 C++ 中,是 const char 数组。 此示例生成 C2440:

// C2440s.cpp
// Build: cl /Zc:strictStrings /W3 C2440s.cpp
// When built, the compiler emits:
// error C2440: 'initializing' : cannot convert from 'const char [5]'
// to 'char *'
//        Conversion from string literal loses const qualifier (see
// /Zc:strictStrings)

int main() {
   char* s1 = "test"; // C2440
   const char* s2 = "test"; // OK
}

C++20 u8 文本是 const char8_t

在 C++20 或 /Zc:char8_t 下,UTF-8 文本字符或字符串(例如 u8'a'u8"String")分别属于 const char8_tconst char8_t[N] 类型。 此示例演示如何在 C++17 和 C++20 之间更改编译器行为:

// C2440u8.cpp
// Build: cl /std:c++20 C2440u8.cpp
// When built, the compiler emits:
// error C2440: 'initializing' : cannot convert from 'const char8_t [5]'
// to 'const char *'
// note: Types pointed to are unrelated; conversion requires
// reinterpret_cast, C-style cast or function-style cast)

int main() {
   const char* s1 = u8"test"; // C2440 under /std:c++20 or /Zc:char8_t, OK in C++17
   const char8_t* s2 = u8"test"; // OK under /std:c++20 or /Zc:char8_t, C4430 in C++17
   const char* s3 = reinterpret_cast<const char*>(u8"test"); // OK
}

指向成员的指针

如果尝试将指向成员的指针转换为 void*,可能会看到 C2440。 下一个示例生成 C2440:

// C2440.cpp
class B {
public:
   void  f(){;}

   typedef void (B::*pf)();

   void f2(pf pf) {
       (this->*pf)();
       void* pp = (void*)pf;   // C2440
   }

   void f3() {
      f2(f);
   }
};

未定义类型的强制转换

如果尝试从仅前向声明但未定义的类型进行强制转换,编译器会发出 C2440。 此示例生成 C2440:

// c2440a.cpp
struct Base { }; // Defined

struct Derived; // Forward declaration, not defined

Base * func(Derived * d) {
    return static_cast<Base *>(d); // error C2440: 'static_cast' : cannot convert from 'Derived *' to 'Base *'
}

不兼容的调用约定

下一个示例的第 15 行和第 16 行上的 C2440 错误由 Incompatible calling conventions for UDT return value 消息限定。 UDT 是用户定义的类型,例如类、struct 或联合。 当前向声明的返回类型中指定的 UDT 的调用约定与 UDT 的实际调用约定冲突以及涉及函数指针时,会导致此类不兼容错误。

在此示例中,首先有一个 struct 和一个返回 struct 的函数的前向声明。 编译器假定 struct 使用 C++ 调用约定。 接下来是 struct 定义,该定义默认使用 C 调用约定。 由于编译器在完成读取整个 struct 之后才知道 struct 的调用约定,所以 get_c2 的返回类型中 struct 的调用约定也被假定为 C++。

struct 后跟另一个返回 struct 的函数声明。 此时,编译器知道 struct 的调用约定是 C++ 。 同样,返回 struct 的函数指针在 struct 定义之后定义。 编译器现在了解 struct 使用 C++ 调用约定。

若要解决由不兼容调用约定引起的 C2440 错误,请声明在 UDT 定义后返回 UDT 的函数。

// C2440b.cpp
struct MyStruct;

MyStruct get_c1();

struct MyStruct {
   int i;
   static MyStruct get_C2();
};

MyStruct get_C3();

typedef MyStruct (*FC)();

FC fc1 = &get_c1;   // C2440, line 15
FC fc2 = &MyStruct::get_C2;   // C2440, line 16
FC fc3 = &get_C3;

class CMyClass {
public:
   explicit CMyClass( int iBar)
      throw()   {
   }

   static CMyClass get_c2();
};

int main() {
   CMyClass myclass = 2;   // C2440
   // try one of the following
   // CMyClass myclass{2};
   // CMyClass myclass(2);

   int *i;
   float j;
   j = (float)i;   // C2440, cannot cast from pointer to int to float
}

将零分配给内部指针

如果将零分配给内部指针,也会引发 C2440:

// C2440c.cpp
// compile with: /clr
int main() {
   array<int>^ arr = gcnew array<int>(100);
   interior_ptr<int> ipi = &arr[0];
   ipi = 0;   // C2440
   ipi = nullptr;   // OK
}

用户定义的转换

错误使用用户定义的转换也可能引发 C2440。 例如,当转换运算符被定义为 explicit 时,编译器无法在隐式转换中使用它。 有关用户定义转换的详细信息,请参阅用户定义转换 (C++/CLI)。 此示例生成 C2440:

// C2440d.cpp
// compile with: /clr
value struct MyDouble {
   double d;
   // convert MyDouble to Int32
   static explicit operator System::Int32 ( MyDouble val ) {
      return (int)val.d;
   }
};

int main() {
   MyDouble d;
   int i;
   i = d;   // C2440
   // Uncomment the following line to resolve.
   // i = static_cast<int>(d);
}

System::Array 创建

如果尝试在 C++/CLI 中创建类型为 Array 的数组实例,也会引发 C2440。 有关详细信息,请参阅 array。 下一个示例生成 C2440:

// C2440e.cpp
// compile with: /clr
using namespace System;
int main() {
   array<int>^ intArray = Array::CreateInstance(__typeof(int), 1);   // C2440
   // try the following line instead
   // array<int>^ intArray = safe_cast<array<int> ^>(Array::CreateInstance(__typeof(int), 1));
}

特性

C2440 也可能因为属性功能的变化而发生。 以下示例生成 C2440。

// c2440f.cpp
// compile with: /LD
[ module(name="PropDemoLib", version=1.0) ];   // C2440
// try the following line instead
// [ module(name="PropDemoLib", version="1.0") ];

组件扩展向下强制转换

/clr 下编译源代码时,Microsoft C++ 编译器不再允许 const_cast 运算符向下强制转换。

若要解析此 C2440,请使用正确的强制转换运算符。 有关详细信息,请参阅强制转换运算符

此示例生成 C2440:

// c2440g.cpp
// compile with: /clr
ref class Base {};
ref class Derived : public Base {};
int main() {
   Derived ^d = gcnew Derived;
   Base ^b = d;
   d = const_cast<Derived^>(b);   // C2440
   d = dynamic_cast<Derived^>(b);   // OK
}

一致性模板匹配更改

由于 Visual Studio 2015 Update 3 中编译器的一致性更改,可能会引发 C2440。 以前,编译器在标识 static_cast 操作的模板匹配时会错误地将某些不同的表达式视为相同类型。 现在编译器可以正确区分类型,并且依赖于先前 static_cast 行为的代码被破坏。 若要解决此问题,请更改模板参数以匹配模板参数类型,或者使用 reinterpret_cast 或 C 样式的强制转换。

此示例生成 C2440:

// c2440h.cpp

template<int *a>
struct S1 {};

int g;
struct S2 : S1<&g> {
};

int main()
{
    S2 s;
    static_cast<S1<&*&g>>(s); // C2440 in VS 2015 Update 3
    // This compiles correctly:
    // static_cast<S1<&g>>(s);
}

此错误可能显示在 ATL 代码中,该代码使用 <atlcom.h> 中定义的 SINK_ENTRY_INFO 宏。

复制列表初始化

Visual Studio 2017 正确引发了与使用初始值设定项列表创建对象相关的编译器错误。 这些错误在 Visual Studio 2015 中没有被发现,它们可能会导致崩溃或未定义的运行时行为。 在 C++17 复制列表初始化中,编译器需要考虑用于重载解析的显式构造函数,但如果实际选择该重载,则必须引发错误。

以下示例在 Visual Studio 2015 中进行编译,但不在 Visual Studio 2017 中进行编译。

// C2440j.cpp
struct A
{
    explicit A(int) {}
    A(double) {}
};

int main()
{
    const A& a2 = { 1 }; // error C2440: 'initializing': cannot
                         // convert from 'int' to 'const A &'
}

为更正此错误,应使用直接初始化:

// C2440k.cpp
struct A
{
    explicit A(int) {}
    A(double) {}
};

int main()
{
    const A& a2{ 1 };
}

类构造中的 cv 限定符

在 Visual Studio 2015 中,编译器有时会在通过构造函数调用生成类对象时错误地忽略 cv 限定符。 此缺陷可能会导致崩溃或意外的运行时行为。 以下示例在 Visual Studio 2015 中编译,但在 Visual Studio 2017 及更高版本中会引发编译器错误:

struct S
{
    S(int);
    operator int();
};

int i = (const S)0; // error C2440

若要更正此错误,请将运算符 int() 声明为 const。