【问题标题】:Named parameters of integral types through enumeration types通过枚举类型的整数类型的命名参数
【发布时间】:2021-03-30 09:04:46
【问题描述】:

考虑以下代码:

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 风格的强制转换,在 C++ 中你会使用 Widget&lt;ComplexityInt(4)&gt;what are your thoughts 这个论坛力求客观,基于意见的问题是题外话。
  • @paddy,你可以在想象有很多数字参数的情况后尝试重新估计收益,并且参数更有意义,并且在某个遥远的标题中定义类,并且你正在不断发展和更改类定义,然后在程序的其他部分使用它。
  • 我不必想象这样的情况。我在维护的一个大型项目中有一个。我有template &lt;int type&gt; class Foo {};,我有一个带有实际名称的枚举。所以我有Foo&lt;kMeaningulName&gt;Foo&lt;kObvious&gt; 。您还没有明确说明为什么不能在示例中使用名称。
  • @KamilCuk,你能指出一段我可以读到这种初始化的段落吗?在 cppreference.com 上,我只发现 “如果以下所有条件都为真,则可以使用列表初始化从一个整数初始化其基础类型固定的枚举类型,而无需强制转换...” (C++17 起)。

标签: c++ enums parameters named


【解决方案1】:

有 2 个不同/正交的事情需要考虑(我以 Rectangle 为例):

  • 您要命名参数吗? (如Height/Width

命名参数以一些冗长和额外类型为代价增加了更好的表现力。 这些类型可能只存在于通过 IMO 显式传递参数。

转置矩形将交换heightwidth

  • 你想表达一个类型吗? (如Length

额外类型允许更安全,但以额外类型为代价,可能还有一些“重复”功能(Length 应该是可添加的,(他们的产品提供Area),但添加Weight 也是可能的,复制添加它们的基础类型)。

作为constType 可能需要在代码中传播。

  • 两者都有?

你可以:-) 由于概念是正交的。它不是让Height 包装int/double,而是包装Length

【讨论】:

    【解决方案2】:

    让我们暂时忽略它是否有效。

    使用enum,您可能会提高——即使我个人不同意——调用方的可读性。

    但从与 Widget 代码本身相关的代码的角度来看,如果这些 enum 实际上表示具有任意值的积分,它们看起来是错误的和令人困惑的。

    如果complexitysize 的值被限制在某个范围内,最好为它们定义一个专用类型。

    如果你想进入像 enum 示例这样的方向,我会改用 using

    using ComplexityInt = int;
    using SizeInt = int;
    
    template<ComplexityInt complexity>
    class Widget 
    {
    public:
        Widget(SizeInt size);
    };
    
    int main()
    {
        Widget<ComplexityInt(4)> widget( SizeInt(12) );
        return 0;
    }
    

    【讨论】:

    • 一个缺点是您可能会弄乱顺序,特别是当您在不断开发和更改定义并且还在程序的另一部分使用此类时。像这样Widget&lt;SizeInt(4)&gt; widget( ComplexityInt(12) );
    • @oleksijp 是的。如果您可以使用c++20,您可以为所需的运算符定义一个实际的类模板。并根据它们为SizeIntComplexityInt 创建类型。如果您确实有明显的运行时成本(我认为不应该),您可以将宏用于生产代码,将这些类型直接映射到 int
    • enumint 输入更多。我们在这里需要一个“strong_typedef”(正如 boost 所提供的),而不是一个简单的别名。
    【解决方案3】:

    我相信这些转换是未定义的行为,因为这些整数超出了枚举值的范围。来自 C++20 草案 [expr.static.cast]/10:

    如果枚举类型没有固定的底层 type,如果原始值在枚举值(9.7.1)的范围内,则值不变,并且 否则,行为未定义

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

    对于任何体面的编辑器,将鼠标悬停在类名上(或按住 ctrl 单击以导航到其定义)将显示定义以及 complexity 参数的名称。构造函数及其size 参数也是如此。

    做任何这种奇怪的铸造都会让读者想知道发生了什么,而不是继续阅读代码。

    【讨论】:

    • 但问题中的基础类型是固定的。
    【解决方案4】:

    正如其他人已经指出的那样,强制转换是一种类型安全违规,应尽可能避免,尤其是 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
    }
    

    【讨论】:

    • @oleksa 比其他标准更喜欢类型安全。编辑了我的答案以解决您的主要问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多