19 July 2022

C++ 核心指南目录

F.55: Don’t use va_arg arguments

理由

va_arg读取参数的时候,我们假设实际参数类型是正确设置的。传递变长参数的时候,我们假定函数会正确解析参数类型。但是两种假定都不一定可靠。

例子

// -*- compile-command: "g++ -std=c++20 code.cpp && ./a"; -*-
#include <iostream>
#include <gsl/gsl>
#include <cstdarg>
using namespace std;
using namespace gsl;
int sum(int cnt ...)
{
    int result = 0;
    va_list list;
    va_start(list, cnt);
    while (cnt > 0) {
        cnt--;
        result += va_arg(list, int); // BAD, assumes it will be passed ints
    }
    va_end(list);
    return result;
}

int main()
{
    cout << sum(2, 3, 2) << endl; // ok
    cout << sum(2, 3.14159, 2.71828) << endl; // BAD, undefined
}
5
-2050589186

如果用C++17的折叠表达式(fold expression)对 Args 进行折叠展开,就比较灵活了:

// -*- compile-command: "g++ -std=c++20 code.cpp && ./a"; -*-
#include <iostream>
#include <gsl/gsl>
using namespace std;
using namespace gsl;
template<class ...Args>
auto sum(Args... args) // GOOD, and much more flexible
{
    return (... + args); // note: C++17 "fold expression"
}
int main()
{
    cout << sum(3, 2) << endl; // ok: 5
    cout << sum(3.14159, 2.71828) << endl; // ok: ~5.85987
    cout << sum(1, 2, 3, 4.8) << endl;
    cout << sum(string("hello"), string(" "), string("world"));
}
5
5.85987
10.8
hello world

其他方法

  • 重载( overloading )
  • 可变参数模板(variadic templates)
  • 变体参数(variant arguments)
  • 初始化列表(initializer_list)

注意

声明一个...参数,表示接收所有参数,就可以避免写明实际多少参数,减少不必要的函数重载。

强化

  • 如果代码中使用了va_list, va_start, va_arg,对其进行诊断。
  • 如果以va_arg的形式传递多个参数给函数,但是没指明什么类型,考虑是否可以通过重载明确参数类型。