以临时方式使用 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)
这是行不通的,我不打算与编译器争论,但我将其包含在这里是为了帮助说明我对此声明的目标。我的规格:
- 将单个传递
GLenum
限制为一组三个当前支持的类型。最好以临时方式,例如不需要太多样板文件。
- 对于用户,可以这样调用此函数:
create_shader("my/path", GL_FRAGMENT_SHADER)
。看不到模板。
此代码的主要问题是:
declaration of 'shader_type' shadows a template parameter
。我可以看出这是因为在模板参数和函数参数中都指定了 GLenum shader_type
,但我希望编译器能以某种方式从函数参数中推断出类型。
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
作为模板参数传递。这真的很好,但它引出了我的问题:
- 有没有办法像我在约束方面所做的那样,但是 没有 让用户明确地将
shader_type
作为模板参数传递,而不是更喜欢函数参数?
- 有没有办法用更少的
requires
来写这个声明?我怀疑不是,但如果我能剪出一个就好了。
- 一般来说,有没有人知道总体上“更好”的替代方案。也许在这个问题的顶部提到我的规范,了解“更好”可能是什么。
附录:
- 我知道
constexpr
并且我可能会在函数内部进行检查,但是作为一个学习者,通过约束和概念来进行检查对我来说也是一种探索。
- 如果人们对
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
变量)。
此代码的目标是将函数参数的传递值限制为 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)
这是行不通的,我不打算与编译器争论,但我将其包含在这里是为了帮助说明我对此声明的目标。我的规格:
- 将单个传递
GLenum
限制为一组三个当前支持的类型。最好以临时方式,例如不需要太多样板文件。 - 对于用户,可以这样调用此函数:
create_shader("my/path", GL_FRAGMENT_SHADER)
。看不到模板。
此代码的主要问题是:
declaration of 'shader_type' shadows a template parameter
。我可以看出这是因为在模板参数和函数参数中都指定了GLenum shader_type
,但我希望编译器能以某种方式从函数参数中推断出类型。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
作为模板参数传递。这真的很好,但它引出了我的问题:
- 有没有办法像我在约束方面所做的那样,但是 没有 让用户明确地将
shader_type
作为模板参数传递,而不是更喜欢函数参数? - 有没有办法用更少的
requires
来写这个声明?我怀疑不是,但如果我能剪出一个就好了。 - 一般来说,有没有人知道总体上“更好”的替代方案。也许在这个问题的顶部提到我的规范,了解“更好”可能是什么。
附录:
- 我知道
constexpr
并且我可能会在函数内部进行检查,但是作为一个学习者,通过约束和概念来进行检查对我来说也是一种探索。 - 如果人们对
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
变量)。