通过像printf这样的可变参数函数传递一个带有常量字符*转换函数的类
Passing a class with conversion function for const char* through a variadic function like printf
我有一个类SpecialString。它有一个运算符重载/转换功能,它在任何时候作为常量字符*传递时使用。然后它返回一个普通的 c 字符串。
class SpecialString
{
...
operator char* () const { return mCStr; }
...
};
这在很久以前(实际上是 19 年前)曾经有效,当时我将它们直接传递给 printf()。编译器足够聪明,知道参数是 char* 并且它使用了转换函数,但现在的 g++ 抱怨。
SpecialString str1("Hello"), str2("World");
printf("%s %sn", str1, str2);
错误:无法通过可变参数方法传递非 POD 类型"SPECIALSTRING"(又名"SpecialString")的对象;调用将在运行时中止 [-Wnon-pod-varargs]
有没有办法在不更改代码的情况下让它再次工作?我可以添加一个 deref 运算符重载函数,该函数返回 c 字符串并像这样传递 SpecialString 对象。
class SpecialString
{
...
operator CHAR* () const { return mCStr; }
char* operator * () const { return mCStr; }
...
};
SpecialString str1("Hello"), str2("World");
printf("%s %sn", *str1, *str2);
但我宁愿不要这样做,因为这需要手动更改数千行代码。
如果您不想收到通知,可以禁用该警告...但这是个坏主意。
程序的行为是未定义的,您应该修复它,这需要更改代码。您可以将现有转换运算符与static_cast
一起使用,或者您可以使用一元*
运算符的想法,这将更简洁。
如果您改用不需要引入重载的一元+
,则需要更少的更改,因为它将调用隐式转换。不过,这可能会给代码的读者增加一些困惑。
由于您不想修改现有代码,因此可以编写"hack"。更具体地说,一堆重载printf()
修补现有代码。
例如:
int printf(const char* f, const SpecialString& a, const SpecialString& b)
{
return printf(f, (const char*)a, (const char*)b);
}
在标头中声明此函数后,每次使用这些特定参数调用printf()
都将使用此函数而不是您熟悉的"真实"printf()
,并执行所需的转换。
我认为您的代码中有相当多的printf()
调用组合涉及SpecialString
,因此您可能需要编写一堆不同的重载,这至少可以说是丑陋的af,但它确实符合您的要求。
正如另一条评论中提到的,它一直是未定义的行为,恰好适用于您的情况。
对于MicrosoftCString
类,似乎未定义的行为被如此使用(因为它碰巧有效),现在布局以它仍然可以工作的方式定义。请参阅如何将 CString 传递给格式字符串 %s?。
在我们的代码库中,当我修改文件以通过调用GetString()
显式执行转换时,我尝试修复代码
您可以执行以下几项操作:
-
修复收到警告的任何位置的现有代码。
在这种情况下,像
c_str
或GetString
这样的命名函数比转换运算符更可取,以避免显式强制转换(例如static_cast
甚至最糟糕的 C 型情况(const char *)
.deref
运算符可能是可以接受的折衷方案。 -
使用一些格式库
<iosteam>
- FMT:https://github.com/fmtlib/fmt
- 许多其他选择(搜索C++格式库或类似内容)
-
使用可变参数模板函数,以便可以进行转换。
- 如果只使用几个类型(整数、双精度、字符串),并且很少超过 2 或 3 个参数,则定义重载也可能是可能的。
-
不建议:破解您的课程以再次工作。
- 您是否对类定义进行了任何更改,导致它中断或仅升级编译器版本或更改编译器选项?
- 这种黑客正在使用未定义的行为,因此您必须弄清楚编译器的工作原理,并且代码将不可移植。
- 要使其正常工作,类必须具有指针的大小,并且数据本身必须与指针兼容。因此,从本质上讲,数据必须由单个指针组成(没有向量表或其他东西)。
旁注:我认为应该避免定义自己的字符串类。在大多数情况下,应使用标准C++字符串(或字符串视图)。如果您需要其他函数,我建议您在命名空间中编写独立函数,例如StringUtilities
。这样,您可以避免在自己的字符串和标准字符串(或某些库字符串,如MFC,Qt或其他内容)之间来回转换。
- 函数向量_指针有不同的原型,我可以构建一个吗
- 为什么在没有显式默认构造函数的情况下,将另一个结构封装在联合中作为成员的结构不能编译
- 创建一个函数以在输入为负数或零时输出字符串.第一次执行用户定义的函数
- 基于另一个成员参数将函数调用从类传递给它的一个成员
- 如何仅为一个函数添加延迟
- 构造函数正在调用一个使用当前类类型的函数
- C++-试图将函数指针推回到另一个CPP文件中的矢量时出错
- 有一个打印语句的函数是一种糟糕的编程实践吗
- 有没有什么方法可以使用一个函数中定义的常量变量,也可以由c++中同一程序中的其他函数使用
- 输入到文件并输出到另一个文件,并将流文件传递给函数
- 我不明白为什么我声明一个空的内部结构并将其传递给构造函数
- 如何创建函数管道,以便函数一个接一个地运行?
- 如何巧妙地编写两个函数——一个用于检查是否存在解决方案,另一个用于获取所有解决方案
- 在c++中的复制构造函数/一个声明语句中的初始化的延续中使用chain方法
- C :基类调用自己的虚拟函数 - 一个反图案
- 如何在这个交换函数(一个单独的链表)中找到错误
- 两个相同的函数(一个使用模板模式,另一个不使用)
- 你怎么能一次给一个函数一个参数呢
- 为什么要做两个函数?(一个是非const,另一个是const)
- 当代码在其他地方使用时,如何保证函数一个接一个地被调用