是否可以在不使用数组的情况下将不同数量的相同类型的参数传递给函数?

Can you pass a varying number of arguments of the same type to a function without using arrays?

本文关键字:同类型 参数传递 函数 情况下 是否 数组      更新时间:2023-10-16

我需要从一个最小的文件路径传递到一个函数的最多七个文件路径。有一个约定,其中文件路径本身就足以确定如何处理每个文件。

参数的顺序无关紧要。

处理此问题的一个明显选项(我当前实现的选项(是传递一个空字符串作为未使用插槽的参数。

另一种方法是将参数作为数组或向量传递。

另一个方法是实现所有可能的参数排列(可能的,不实用的(。

我想知道是否有一种方法可以简单地指定参数的数量可以变化,然后简单地传递参数本身。

例如,假设只有一个具有特殊语法的 f(( 实现来表示不同数量的参数

应编译以下内容:

int main()
{
f(file);
f(file1, file2);
f(file1, file3, file2, file6);
}

有没有办法在C++实现这一目标?

您可以使用递归模板函数。

#include <iostream>
template <typename First>
void f(First&& first) {
std::cout << first << std::endl;
}
template <typename First, typename... Rest>
void f(First&& first, Rest&&... rest) {
f(std::forward<First>(first));
f(std::forward<Rest>(rest)...);
}
int main() {
f(6,7,8,9,10);
}

如果你真的需要一个可变的(无限的(参数数

  • 如果您使用的是C++11 或更高版本

    • 使用std::initializer_list(仅当所有类型都相同时( - 请参阅 https://stackoverflow.com/a/16338804/9305398。

    • 使用可变参数模板(即参数包( - 请参阅@super的答案和/或 https://stackoverflow.com/a/16338804/9305398。

  • 如果您使用的是C++03 或更高版本

    • 使用可变参数 -- 见 https://stackoverflow.com/a/1657924/9305398。

否则,如果您有固定数量的(可选(参数

  • 如果您使用的是C++20 或更高版本

    • 使用指定的初始化作为命名参数的一种方式。
  • 如果您使用的是C++03 或更高版本

    • 使用可为空/可选的类型(例如原始指针、boost::optional、C++17 的std::optional...( - 请参阅@NicolBolas的答案。

    • 定义所有必需/逻辑重载(可能使用自定义类型( - 丑陋,但这可以通过外部代码生成器和/或预处理器自动执行。

否则,如果可以使用不同的设计来完成相同的操作,则可以对C++03 及更高版本执行以下任一操作:

  • 按照@PaulMcKenzie的建议将指针传递给struct

  • 设计一个允许设置属性(通过构造函数和/或方法(的类,然后具有成员函数来对该数据执行操作,例如:

    ShaderCompiler sc(vs, fs, ...);
    sc.setGeometryShader(...);
    sc.compile();
    
  • 一种特别好的方式(参见例如QString(是设计一个类,允许执行以下操作:

    result = ShaderCompiler()
    .vertex(...)
    .fragment(...)
    ...
    .compile()
    ;
    
  • 同样,利用依赖于参数的查找:

    Shader()
    << Vertex(...)
    << Fragment(...)
    ...
    ;
    

由于您有一组有限的可能性,因此以下是处理此问题的明显方法:

using opt_path = std::optional<path>;
shader compile_shaders(opt_path vs, opt_path tcs = std::nullopt, opt_path tes = std::nullopt, opt_path gs = std::nullopt, opt_path fs = std::nullopt, opt_path cs = std::nullopt)
{
...
}

这些仅对所有其他着色器路径使用默认参数。您可以通过接口来判断哪些提供了哪些,哪些没有提供std::optional。如果您不使用 C++17,您显然会将其替换为boost::optional或类似类型。

当然,无论您如何处理,都会导致界面明显不佳。考虑一下为了创建最常见的情况而必须做什么:顶点着色器与片段着色器的组合:

compile_shaders(vs_path, std::nullopt, std::nullopt, std::nullopt, fs_path);

用户会记得它们之间有 3 个阶段吗?赔率很大,他们不会。人们会不断犯只使用 2std::nullopt秒或使用 4 秒的错误。考虑到VS + FS是最常见的情况,您有一个界面,其中最常见的情况很容易出错。

现在确定,您可以重新排列参数的顺序,使 FS 成为第二个参数。但是,如果要使用其他阶段,现在必须查找函数的定义,以记住哪些值映射到哪些阶段。至少,我在这里的做法遵循OpenGL的管道。任意映射需要查找文档。

如果要创建计算着色器,则必须记住,必须显式清空6个阶段:

compile_shaders(std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt, cs_path);

将所有这些与更自我描述的界面进行比较:

shader_paths paths(vs_path, shader_paths::vs);
paths.fragment(fs_path);
auto shader = compile_shaders(paths);

这里没有歧义。提供给构造函数的路径使用第二个参数显式声明为顶点着色器。因此,如果你想要一个计算着色器,你可以使用shader_paths::cs来表达它。然后,使用适当命名的函数为路径提供一个片段着色器。在此之后,编译着色器,就完成了。