以临时方式使用 requires 约束 non-type/value 模板参数

Constrain non-type/value template parameter using requires in an ad-hoc fashion

此代码的目标是将函数参数的传递值限制为 select 几种可能性,在 C++20 的编译时进行检查。我最初失败的第一次尝试看起来像这样:

template<GLenum shader_type>
requires requires
{
    shader_type == GL_VERTEX_SHADER ||
    shader_type == GL_FRAGMENT_SHADER ||
    shader_type == GL_COMPUTE_SHADER;
}
GLuint create_shader(std::filesystem::path const& shader_path, GLenum shader_type)

这是行不通的,我不打算与编译器争论,但我将其包含在这里是为了帮助说明我对此声明的目标。我的规格:

  1. 将单个传递 GLenum 限制为一组三个当前支持的类型。最好以临时方式,例如不需要太多样板文件。
  2. 对于用户,可以这样调用此函数:create_shader("my/path", GL_FRAGMENT_SHADER)。看不到模板。

此代码的主要问题是:

  1. declaration of 'shader_type' shadows a template parameter。我可以看出这是因为在模板参数和函数参数中都指定了 GLenum shader_type,但我希望编译器能以某种方式从函数参数中推断出类型。
  2. shader_type == GL_VERTEX_SHADER || shader_type == GL_FRAGMENT_SHADER || shader_type == GL_COMPUTE_SHADER 根本没有完成它的工作。我可以在没有编译时问题的情况下传递 GL_GEOMETRY_SHADER。 (我希望我能在 运行 时间里说同样的话)

看了 之后,我想到了下一个:

template<GLenum shader_type>
requires requires
{
    requires shader_type == GL_VERTEX_SHADER ||
             shader_type == GL_FRAGMENT_SHADER ||
             shader_type == GL_COMPUTE_SHADER;
}
GLuint create_shader(std::filesystem::path const& shader_path)

在将传递的值限制为这些值时,这似乎符合我的期望,但是,它确实需要 shader_type 作为模板参数传递。这真的很好,但它引出了我的问题:

  1. 有没有办法像我在约束方面所做的那样,但是 没有 让用户明确地将 shader_type 作为模板参数传递,而不是更喜欢函数参数?
  2. 有没有办法用更少的requires来写这个声明?我怀疑不是,但如果我能剪出一个就好了。
  3. 一般来说,有没有人知道总体上“更好”的替代方案。也许在这个问题的顶部提到我的规范,了解“更好”可能是什么。

附录:

  1. 我知道 constexpr 并且我可能会在函数内部进行检查,但是作为一个学习者,通过约束和概念来进行检查对我来说也是一种探索。
  2. 如果人们对 GLenum 或任何其他非标准代码感到困惑:
typedef unsigned int GLenum;
typedef unsigned int GLuint;
#define GL_FRAGMENT_SHADER 0x8B30
#define GL_VERTEX_SHADER 0x8B31
#define GL_COMPUTE_SHADER 0x91B9

如果你想约束一个模板参数,一个requires就足够了:

template <GLenum shader_type>
requires(shader_type == GL_VERTEX_SHADER || shader_type == GL_FRAGMENT_SHADER)
void foo() {}

函数参数可以像这样被约束,代价是要求它是一个 compile-time 常量:

struct ShaderType
{
    GLenum value;
    consteval ShaderType(GLenum value) : value(value)
    {
        if (shader_type != GL_VERTEX_SHADER && shader_type != GL_FRAGMENT_SHADER)
            throw "Invalid value!";
         // Normally it's not a good idea to throw pointers,
         // but here it just used to stop the compilation.
    }
};

void foo(ShaderType type) {}

您不能使用 compile-time 构造(如 requires 子句)来约束运行时值(如函数参数)。您想要的不是 compile-time 约束;这是一个运行时合同。

C++ 目前还没有这样的功能。目前你能做的最好的事情是使用只允许特定值的适当枚举(或键入 static constexpr 变量)。