C++ 通过枚举类型指定整数类型的命名参数

C++ 通过枚举类型指定整数类型的命名参数,c++,enums,parameters,named,C++,Enums,Parameters,Named,考虑以下代码: template<int complexity> class Widget { public: Widget(int size); }; int main() { Widget<4> widget(12); // not easily readable if definition of Widget in some faraway header return 0; } 另一种尝试是将转换为枚举类型作为命名参数: enum Co

考虑以下代码:

template<int complexity>
class Widget 
{
public:
    Widget(int size);
};

int main()
{
    Widget<4> widget(12); // not easily readable if definition of Widget in some faraway header
    return 0;
}
另一种尝试是将转换为枚举类型作为命名参数:

enum ComplexityInt : int {};
enum SizeInt : int {};

template<ComplexityInt complexity>
class Widget 
{
public:
    Widget(SizeInt size);
};

int main()
{
    Widget<(ComplexityInt) 4> widget((SizeInt) 12);
    return 0;
}

是第二个完全精细C++代码,没有一些不希望的副作用或额外的成本?从样式和可读性的角度来看,您对此有何看法?

我认为这些类型转换是未定义的行为,因为这些整数超出了枚举值的范围。从C++20草稿[expr.static.cast]/10:

如果枚举类型没有固定的基础 类型,如果原始值在枚举值9.7.1的范围内,则该值不变,并且 否则,行为是未定义的

我不同意没有这些强制转换代码是不可读的。我觉得你想得太多了

对于任何一个像样的编辑器,将鼠标悬停在类名上或按住ctrl键单击以导航到其定义将显示该定义以及复杂性参数的名称。构造函数及其大小参数也是如此


做任何这种奇怪的角色转换都会让读者疑惑到底发生了什么,而不是仅仅继续阅读代码。

让我们暂时忽略这一点,不管它是否有效

使用枚举,您可能会提高调用方的可读性,即使我个人不同意这一点

但是,从与小部件代码本身相关的代码的角度来看,如果这些枚举实际上表示具有任意值的整数,那么这些枚举看起来是错误的和混乱的

如果复杂性和大小的值被限制在某个范围内,那么最好为它们定义一个专用类型

如果您想进入像您的枚举示例那样的方向,我将使用using


有2个不同的/正交的事物要考虑:以矩形为例:

是否要命名参数?作为高度/宽度 命名参数增加了更好的表达能力,但代价是一些冗长和额外的类型。 这些类型可能只存在于通过IMO显式传递参数的情况下

变换矩形将交换高度和宽度

你想表达一种类型吗?作为长度 额外类型允许更安全,但要以额外类型为代价,并且可能有些重复的函数长度应该是可添加的,它们的产品提供了面积,但也可以添加权重,从而重复添加它们的基础类型

作为常量,类型可能需要在代码中传播

二者都 你可以:-
因为概念是正交的。与用高度包装int/double不同,它包装的是长度。

正如其他人已经指出的,强制转换是违反类型安全的行为,应该尽可能避免,尤其是c样式强制转换。语法xxx或xxx并不重要,因为您无法控制编译器的实际操作。参数修饰不是绕过语言类型安全性的有效理由。 那么撇开这个不谈,

Widget</*Complexity*/ 4> widget(/*Size*/ 12);
如果您需要一些编译器支持,对于int参数,您可以安全地执行以下操作:

struct Size
{
    constexpr explicit Size(const int v) noexcept : value(v) {}
    constexpr operator int() const noexcept { return value; }
    int value;
};

template<int complexity>
class Widget 
{
public:
    Widget(Size size) {}
};

int main()
{
    //Widget<4> widget(12); // Error: 12 is not a Size
    Widget<4> widget(Size{12}); // Ok
}
代码是自我解释的,我确信这不会增加开销,二进制输出将是相同的。 关于模板参数,事情有点微妙。 假设您有有限数量的可能复杂性,您可以这样做:

enum class Complexity { min=1, mid, max, super };

template<Complexity complexity>
class Widget 
{
public:
    Widget(Size size) {}
};

int main()
{
    //Widget<4> widget(Size{12}); // Error, 4 is not a Complexity
    Widget<Complexity::super> widget(Size{12}); // Ok
}

因为您只是将整数强制转换为没有实际命名值的枚举类型,所以我看不到第二个示例有任何好处。这两个例子都没有真正意义,因为不清楚小部件是什么,或者这些值与它有什么关系。我会使用一个代码更少的代码。不要使用C样式的c++,在C++中你会做小部件。您的想法是什么?本论坛力求客观,基于观点的问题是离题的。@paddy,您可以在想象有大量数值参数、参数更有意义以及类定义在某个遥远的标题中的情况后,尝试重新估计收益,您正在不断地开发和更改类定义,并在程序的其他部分使用它。我不必想象这种情况。我维护的一个大项目中有一个。我有模板类Foo{};我有一个实际名字的枚举。所以我有Foo,Foo等等。你还没有明确说明为什么你不能在你的例子中使用名称。@KamilCuk,你能指出一段我可以阅读的关于这种初始化的内容吗。在cppreference.com上,我只发现,如果以下所有条件都为真,则可以使用列表初始化从整数初始化基础类型为固定的枚举类型,而无需强制转换。。。由于C++17.0,一个缺点是您可能会打乱顺序,特别是当您在不断开发和更改定义,并且在程序的另一部分中也使用此类时。类似于这个小部件ComplexityInt12@奥列克西普:没错。如果可以使用c++20,则可以为网元定义实际的类模板
eded操作员。并基于它们创建SizeInt和ComplexityInt的类型。如果您确实有明显的运行时成本,我认为这是不应该的,那么您可以在生产代码中使用一个宏,将这些类型直接映射到int。enum的类型比int更多。我们需要一个boost提供的强类型定义,而不是一个简单的别名。但问题中的基本类型是固定的。@oleksa更喜欢类型安全性而不是其他标准。编辑我的答案以解决您的主要问题。
enum class Complexity { min=1, mid, max, super };

template<Complexity complexity>
class Widget 
{
public:
    Widget(Size size) {}
};

int main()
{
    //Widget<4> widget(Size{12}); // Error, 4 is not a Complexity
    Widget<Complexity::super> widget(Size{12}); // Ok
}