c++ - MFC CString 中的可变参数和转换运算符

标签 c++ mfc variadic-functions

在我测试过的所有 Visual C++ 版本下的 MFC 中,以下代码都可以编译并运行;

CString A = "String A", B;
B.Format("The value of A is %s", A);

但产生警告

C6284, Object passed as _Param_(2) when a string is required in call to 
'ATL::CStringT<char,StrTraitMFC<char,ATL::ChTraitsCRT<char> > >::Format'
 Actual type: 'class ATL::CStringT<char,class StrTraitMFC<char,class ATL::ChTraitsCRT<char> > >'.

查看 CString 的源代码及其 CStringT 的祖先 CSimpleStrT,同时有一个转换运算符可以有效地转换为 char*,如下所示;

operator PCXSTR() const throw()
{
    return( m_pszData );
}

数据缓冲区似乎也是类中的第一个数据元素,通过调试器下的执行跟踪表明未调用转换运算符,代码纯粹基于类布局工作。

虽然可以很容易地重写原始代码来避免上述情况,但是否有必要并且可能会被 future 的 MFC 更新破坏?所涉及的代码库部分主要是第 3 方,我宁愿避免在没有充分理由的情况下更改它。

最佳答案

不,您编写的代码已损坏。正式地说,这是未定义的行为。您不应将对象传递给可变参数函数。这些类型的函数没有编译时类型检查(,不是类型安全的),因此编译器不知道它应该调用隐式转换运算符来转换 CString 对象到 PCXSTR。您必须显式执行转换,要么使用强制转换,要么调用返回指向底层 C 样式字符串缓冲区的指针的成员函数。

对于所有 可变参数函数都是如此。甚至像 printf 这样简单的东西。以下代码是错误的:

std::string str = "world";
printf("Hello, %s", str);   // <-- this code is WRONG!

你必须这样写:

std::string str = "world";
printf("Hello, %s", str.c_str());

对于 MFC* 也是一样的:

CString str = TEXT("world");
CString buffer;
buffer.Format(TEXT("Hello, %s"), static_cast<LPCTSTR>(str));
// alternatively:
// buffer.Format(TEXT("Hello, %s"), str.GetString());

这是不在 C++ 中使用可变参数函数的一个很好的理由。首选流,它是类型安全的并且做正确的事情。

您收到的警告试图提醒您注意这个问题。尽管可变参数函数不是类型安全的,但这是一个如此普遍的问题,以至于编译器供应商付出了很多努力来尝试解析格式字符串、查找插入并将其与传递的参数进行匹配。由于我所描述的原因,它在这种情况下发现不匹配,并发出警告 C6284 .


* 实际上,在这种情况下,它恰好对 CString 有效。无论如何。这是因为,正如您在调试器中发现的那样,该类经过专门设计,因此它的第一个成员是指向 C 样式字符串缓冲区的指针。因此,当它以非类型安全的方式按值传递给像 printf 这样的可变参数函数时,printf 唯一看到的就是指针,因此它会被解析当它看到 %s 说明符时正确。但我不建议依赖这种行为。它在形式上仍然是未定义的行为,即使它以特定于实现的方式工作。

Microsoft's documentation specifically tells you to pass CString objects to variadic functions by performing an explicit cast to a char-pointer .

诚然,此时 CString 的接口(interface)不太可能发生变化,更不可能因为它会破坏如此多的现有代码。但你永远不知道,当你正确编写代码时,它会让你的意图更加清晰。

关于c++ - MFC CString 中的可变参数和转换运算符,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38850913/

相关文章:

c++ - 为什么 FormatMessage 只为 ERROR_SYSTEM_PROCESS_TERMINATED 和 ERROR_UNHANDLED_EXCEPTION 系统错误创建部分消息?

c++ - 用于交叉编译和CMake的预处理器宏的单元测试

c++ - 将 Boost Spirit 递归匹配结果保存到 C++ 结构

C++ 内联字符串格式化和数字转换

c++ - 将调整大小的位图文件设置为 MFC 优化校准

c++ - 如何在 printf 上创建包装器?

Cython 等效于 c define #define myfunc(node x,...) SetNode(x.getattributeNode(),__VA_ARGS__)

c++ - 如何使用visual studio和MFC C++在windows上实现预览

c++ - 如何防止不同插件中出现重复的资源 ID?

C++ 非类型参数包扩展