代码之家  ›  专栏  ›  技术社区  ›  Drag-On

检测编译时文本和常量

  •  8
  • Drag-On  · 技术社区  · 8 年前

    假设我想编写一个泛型类,它维护一个始终保持在两个值之间的整数。像这样的:

    template<int Lower, int Upper>
    class MyInt {
        private:
            int m_int;
        public:
            // Constructors, operators...
    };
    

    类不变量是 Lower <= m_int <= Upper 。 当然,myint应该具有整数通常所具有的所有操作,比如赋值和算术运算符。如果操作使myint处于破坏其不变量的状态,则myint将抛出。然而,在许多情况下,这应该是编译时可检测的。请考虑以下示例代码:

    int foo = 500;
    constexpr int const bar = 500;
    MyInt<0,100> a = 15; // OK
    MyInt<0,100> b = foo; // Throws at runtime
    MyInt<0,100> c = 500; // Compile error?
    MyInt<0,100> d = bar; // Compile error?
    MyInt<0,100> f = std::integral_constant<int, 500>; // Compile error
    

    为了 std::integral_constant ,直接编写适当的构造函数。但是编译时是否可以检测到 a 在范围内 c d 不是?赋值要么是编译时已知的文本,要么是constexpr常量。

    我试过使用sfinae等等,但是我找不到从值语义到模板参数的方法,即使在(我声称)这些值显然是编译时常量的情况下。

    对于C++ 17来说,语言不提供实现该工具所需的工具是真的吗?如果是,这会随着即将到来的C++ 20而改变吗?如果我要找的是不可能的,原因是什么?我的兴趣完全是教育性的,所以如果我遗漏了一些东西(比如文字实际上不是编译时常量或其他东西),我会感兴趣的。

    注:我知道那个案子 f 通过引入自定义文字后缀并要求用户键入以下内容,可以使其不那么难看:

    MyInt<0,100> g = 15_constint; // OK
    MyInt<0,100> h = 500_constint; // Compile error
    

    我也知道提供这样一个接口的可能性:

    MyInt<0,100> i;
    i.assign<500>(); // Compile error
    

    然而,这两种解决方案都有一定的打印开销,不一定是惯用的C++。

    2 回复  |  直到 8 年前
        1
  •  9
  •   Nicol Bolas    8 年前

    如果我要找的是不可能的,原因是什么?

    基本上归结为这样一个事实,即C++函数模型不允许一个函数识别一个 constexpr 参数和运行时参数。如果一个函数 int ,然后需要 int . 基于是否 int 由常量表达式(如文本)或运行时值提供。

    即使是在 常量表达式 功能 . 你不能有一个 常量表达式 可以对其参数进行编译时检查的函数。评估可以在编译时完成,但是 foo(500); 最终必须表现得和做的一样 auto b = 500; foo(b); .

    这就是为什么所有的解决方案都涉及到使用技巧将常量表达式放入模板参数中,这样的识别是可能的。当然,模板参数可以 从未 作为一个运行时值,这样会产生其他问题(比如必须有多个函数)。作为一个模版的论点是一件很痛苦的事情。

    本质上,你想要的是一个函数可以声明一个参数是 常量表达式 (以及重载规则,允许您拥有- 常量表达式 版本)。没有这样的提案被投票到C++ 20中。做得最好的是 P1045 ,委员会甚至还没有讨论过,还有一堆漏洞需要填补。所以我不会屏住呼吸。

        2
  •  6
  •   bartop    8 年前

    试试这个。这是一个非常简单的可扩展示例:

    template<int Max, int Min>
    class MyInt
    {
    public:
        constexpr MyInt(int i) : m_int(i > Max ? throw std::exception("out of bouonds int") : ( i < Min ? throw std::exception("out of bouonds int") : i)) {}
    private:
        int m_int;
    };
    
    
    int main()
    {
        MyInt<1, 2> i(4);
    }
    

    它显示编译时错误:)。我用这个问题作为指导: constexpr constructor with compile time validation