考虑此代码片段:
struct My {
operator const char*()const{ return "my"; }
} my;
CStringA s( "aha" );
printf("%s %s", s, my );
// another variadic function to get rid of comments about printf :)
void foo( int i, ... ) {
va_list vars;
va_start(vars, i);
for( const char* p = va_arg(vars,const char*)
; p != NULL
; p=va_arg(vars,const char*) )
{
std::cout << p << std::endl;
}
va_end(vars);
}
foo( 1, s, my );
此代码段会产生“直观”输出“aha”。但我不知道这是如何工作的:
CStringA*
的{{1}} const char*
,为什么不能为我自己的类调用呢?有人可以解释一下吗?
编辑:添加了一个虚拟的可变参数函数,将其参数视为operator (const char*)
s。看哪 - 当它到达const char*
论点时它甚至会崩溃......
答案 0 :(得分:7)
C ++ 98标准§5.2.2/ 7的相关文本:
对参数表达式执行左值到右值(4.1),数组到指针(4.2)和函数到指针(4.3)标准转换。在这些转换之后,如果参数没有算术,枚举,指针,成员指针或类类型,则程序格式错误。如果参数具有非POD类类型(第9节),则行为未定义。
所以正式行为未定义。
但是,给定的编译器可以提供任意数量的语言扩展,而Visual C ++则可以。对于将参数传递给...
,MSDN Library记录了Visual C ++的行为,如下所示:
- 如果实际参数的类型为float,则在函数调用之前将其提升为double类型。
- 使用整数提升将任何有符号或无符号的char,short,枚举类型或位字段转换为signed或unsigned int。
- 类类型的任何参数都通过值作为数据结构传递;副本是通过二进制复制而不是通过调用类的复制构造函数(如果存在)来创建的。
这里没有提到任何关于应用用户定义转换的Visual C ++。
可能是CString::Format
,这是你真正感兴趣的功能,取决于上面的最后一点。
干杯&amp;第h。,
答案 1 :(得分:6)
您正在做的是未定义的行为,并且是您的编译器提供的非标准扩展或纯粹的运气。我猜测CString将字符串数据存储为结构中的第一个元素,因此从CString
读取就像它是char *
一样,产生一个有效的以空字符结尾的字符串。
答案 2 :(得分:4)
您无法将非POD数据插入到可变参数函数中。 More info
答案 3 :(得分:1)
如果variadic-function调用在它上面调用operator(const char *),为什么它不能为我自己的类做呢?
是的,但您应该在代码中明确地投射它:printf("%s", (LPCSTR)s, ...);
。
答案 4 :(得分:1)
如果将variadic-function调用转换为推送参数的指针,......
这不是可变函数的工作原理。在内置类型的特殊转换规则(例如char到int)之后,传递参数的值而不是参数的指针。
C ++03§5.2.2p7:
当给定参数没有参数时,参数的传递方式是接收函数可以通过调用va_arg(18.7)来获取参数的值。在参数表达式上执行左值到右值(4.1),数组到指针(4.2)和函数到指针(4.3)标准转换。在这些转换之后,如果参数没有算术,枚举,指针,成员指针或类类型,则程序格式错误。如果参数具有非POD类类型(第9节),则行为未定义。如果参数具有由积分促销(4.5)或浮点促销(4.6)限制的浮点类型的积分或枚举类型,则参数的值将在调用之前转换为提升类型。这些促销被称为默认参数促销。
特别是从上面的内容:
如果参数具有非POD类类型(第9节),则行为未定义。
C ++用于定义va_arg,C99TC3§7.15.1.2p2说:
...如果type与实际的下一个参数的类型不兼容(根据默认参数提升而提升),则行为是未定义的,除了以下情况:[此处不适用的案例列表]
因此,如果传递类类型,它必须是POD,并且接收函数必须应用正确的类型,否则行为是未定义的。这意味着在最坏的情况下,它可能完全按照您的预期工作。
Printf不会为任何用户定义的类类型应用正确的类型,因为它不知道它们,因此您不能将任何UDT类类型传递给printf。你的foo通过使用char指针而不是正确的类类型来做同样的事情。
答案 5 :(得分:1)
没有。它甚至不会调用operator const char*
。 Visual C ++只是将类数据传递给printf
,就像memcpy
一样。它的工作原因是CString
类的布局:它只包含一个成员变量,它是指向字符数据的指针。
答案 6 :(得分:0)
您的printf声明错误:
printf("%s", s, my );
应该是:
printf("%s %s", s, my);
将打印出“啊哈我的”。
CString有const char*
的转化运算符(实际上LPCTSTR
为const TCHAR*
- CStringA
具有LPCSTR
的转换函数。< / p>
printf
调用不会将您的CStringA
对象转换为CStringA*
指针。它基本上将其视为void*
。在CString的情况下,它是纯粹的运气(或者可能是微软的开发人员利用不符合标准的东西的设计),它将为您提供以NULL结尾的字符串。如果您使用_bstr_t
代替(首先使用字符串的大小),尽管具有转换功能,但它会失败。
在调用printf
(或任何可变函数)时,明确地将对象/指针强制转换为您想要的对象是一种很好的做法(在很多情况下都是必需的)。