我必须在嵌入式应用程序中使用IAR编译器(它没有名称空间,异常,多重/虚拟继承,模板受位限制并且仅支持C ++ 03)。
我无法使用参数包,因此尝试使用可变参数创建成员函数。
我知道可变参数通常是不安全的。但是在this
宏中使用va_start
指针是否安全?
如果我使用普通的可变参数函数,则在...
之前需要一个伪参数才能访问其余参数。我知道可变参数宏在...
之前不需要参数,但我不希望使用它。
如果我使用成员函数,它在this
之前隐藏了...
参数,所以我尝试了。:
struct VariadicTestBase{
virtual void DO(...)=0;
};
struct VariadicTest: public VariadicTestBase{
virtual void DO(...){
va_list args;
va_start(args, this);
vprintf("%d%d%d\n",args);
va_end(args);
}
};
//Now I can do
VariadicTestBase *tst = new VariadicTest;
tst->DO(1,2,3);
tst->DO(1,2,3);
按预期打印123。但是我不确定这是否只是一些随机/未定义的行为。我知道tst->DO(1,2);
会像普通的prinf一样崩溃。我不介意。
答案 0 :(得分:20)
什么都没有在标准中指定该行为,因此此构造仅调用正式的Undefined Behaviour。这意味着它可以在您的实现中正常运行,并在其他实现中导致编译错误或意外结果。
非静态方法必须传递隐藏的this
指针这一事实不能保证va_start
可以使用它。之所以可能如此,是因为在早期,C ++编译器只是将C ++源代码转换为C源代码的预处理器,并且隐藏的this
参数是由预处理器添加的,可供C编译器使用。而且可能是出于 compatibility 原因进行维护的。但是我会尽力避免在关键任务代码中出现这种情况...
答案 1 :(得分:3)
似乎是未定义的行为。如果看一下va_start(ap, pN)
在许多实现中的作用(请检查头文件),它将获取pN的地址,将指针增加pN的大小,并将结果存储在ap中。我们可以合法地查看&this
吗?
我在这里找到了很好的参考文献:https://stackoverflow.com/a/9115110/10316011
引用2003 C ++标准:
5.1 [expr.prim]关键字,它命名一个指向对象的指针,该对象将为其调用非静态成员函数(9.3.2)。 ...表达式的类型是指向函数类(9.3.2)的指针,...表达式是一个右值。
5.3.1 [expr.unary.op]一元&运算符的结果是指向其操作数的指针。操作数应为左值或qualified_id。
因此,即使这对您有用,也不能保证,您也不应该依赖它。
答案 2 :(得分:2)
我认为应该没问题,尽管我怀疑您会从C ++标准中找到特定的引用。
基本原理是:va_start()
必须传递给函数的最后一个参数。没有显式参数的成员函数只有一个参数(this
),因此必须是它的最后一个参数。
如果您曾经在无法运行的平台上进行编译(这似乎不太可能,但是您又已经在某种非典型的平台上进行编译),那么添加单元测试就可以提醒您。 >
答案 3 :(得分:0)
这是未定义的行为。由于该语言不需要将this
作为参数传递,因此可能根本不传递。
例如,如果编译器可以确定对象是单例对象,则可以避免在明确要求this
的地址时将this
作为参数传递并使用全局符号(例如对于va_start)。从理论上讲,编译器可能会生成代码来补偿va_start
中的代码(毕竟,编译器知道这是一个单例),但是标准并不需要这样做。
想像这样:
class single {
public:
single(const single& )= delete;
single &operator=(const single& )= delete;
static single & get() {
// this is the only place that can construct the object.
// this address is know not later than load time:
static single x;
return x;
}
void print(...) {
va_list args;
va_start (args, this);
vprintf ("%d\n", args);
va_end (args);
}
private:
single() = default;
};
某些编译器(例如clang 8.0.0)会为上述代码发出警告:
prog.cc:15:23: warning: second argument to 'va_start' is not the last named parameter [-Wvarargs] va_start (args, this);
尽管有警告it runs ok。总的来说,这什么也没有证明,但是发出警告是一个坏主意。
注意:我不知道有任何编译器可以检测单例并对其进行特殊处理,但是该语言并不禁止这种优化。如果您的编译器今天没有执行此操作,则明天可能由另一个编译器完成。
注意2:,尽管如此,将其传递给va_start
还是可行的。即使可行,也不建议执行标准无法保证的事情。
注释3 :不能对以下参数应用相同的单例优化:
void foo(singleton * x, ...)
无法优化,因为它可能具有两个值之一,指向单例或为nullptr
。这意味着此优化问题不适用于此处。