简体   繁体   English

检测编译时文字和常量

[英]Detect compile-time literals and constants

Say I wanted to write a generic class that maintains an integer that always stays between two values. 假设我想编写一个通用类,它维护一个始终保持在两个值之间的整数。 Something like this: 像这样的东西:

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

The class invariant is that Lower <= m_int <= Upper . 类不变量是Lower <= m_int <= Upper Of course MyInt should have all the usual operations that integers tend to have, like assignment and arithmetic operators. 当然,MyInt应该具有整数所有的常用操作,比如赋值和算术运算符。 MyInt would throw if an operation were to leave it in a state that breaks its invariant. 如果操作将其置于打破其不变量的状态,MyInt将抛出。 In many cases, however, this should be compile-time detectable. 但是,在许多情况下,这应该是编译时可检测的。 Consider this example code: 考虑这个示例代码:

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

For std::integral_constant , writing the appropriate constructor is straight-forward. 对于std::integral_constant ,编写适当的构造函数是直截了当的。 But is it possible to compile-time-detect that a is within range and c and d aren't? 但是有可能编译时检测a在范围内而cd不是吗? The assigned values are either literals known at compile time or constexpr constants. 指定的值是编译时已知的文字或constexpr常量。

I have tried SFINAE-ing around and whatnot, but I could not find a way to go from value-semantics to template arguments, even for these cases where (I claim) the values are clearly compile-time constants. 我已经尝试了SFINAE-ing around和whatnot,但我找不到从值语义到模板参数的方法,即使对于这些情况,我声称这些值显然是编译时常量。

Is it true for C++17 that the language does not provide the tools required to implement this? 对于C ++ 17来说,语言是否真的没有提供实现它所需的工具? If yes, is this going to change with the upcoming C++20? 如果是的话,即将推出的C ++ 20会改变吗? In case what I'm looking for is impossible, what are the reasons for this? 如果我正在寻找的是不可能的,这是什么原因? My interest is entirely educational, so I would be interested if I'm missing something (like literals not actually being compile-time constants or something). 我的兴趣完全是教育性的,所以如果我遗漏某些东西(比如文字实际上不是编译时常量或其他东西),我会感兴趣。

Note: I am aware that case f can be made less ugly by introducing a custom literal suffix and requiring the user to type something like this: 注意:我知道通过引入自定义文字后缀并要求用户输入如下内容,可以减少案例f难度:

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

I am also aware of the possibility to provide an interface like this: 我也知道有可能提供这样的界面:

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

However both these workarounds come with a certain typing overhead and are not necessarily idiomatic C++. 然而,这两种解决方法都带有一定的打字开销,并不一定是惯用的C ++。

In case what I'm looking for is impossible, what are the reasons for this? 如果我正在寻找的是不可能的,这是什么原因?

Basically it comes down to the fact that the C++ function model does not permit a function to recognize a distinction between a constexpr parameter and a runtime parameter. 基本上,它归结为C ++函数模型不允许函数识别constexpr参数和运行时参数之间的区别。 If a function takes an int , then it takes an int . 如果一个函数接受一个int ,那么它需要一个int That function's behavior cannot be different based on whether that int is supplied by a constant expression (like a literal) or a runtime value. 根据int是由常量表达式(如文字)还是运行时值提供,该函数的行为不能有所不同。

And this is broadly true even for constexpr functions . 即使对于constexpr 功能 ,这也是如此。 You can't have a constexpr function which can do compile-time checks on its parameters. 你不能有一个constexpr函数,它可以对其参数进行编译时检查。 The evaluation can be done at compile-time, but doing foo(500); 评估可以在编译时完成,但是要做foo(500); must ultimately behave the same as doing auto b = 500; foo(b); 最终必须与auto b = 500; foo(b); auto b = 500; foo(b); .

This is why the workarounds all involve using tricks to put the constant expression in a template argument, where such recognition is possible. 这就是为什么变通方法都涉及使用技巧将常量表达式放在模板参数中,这种识别是可能的。 Of course, a template argument can never be a runtime value, so that creates other issues (like having to have multiple functions). 当然,模板参数永远不能是运行时值,因此会产生其他问题(例如必须具有多个函数)。 And being a template argument is a generally painful thing to work with. 作为一个模板参数是一个通常很痛苦的事情。

Essentially, what you want requires that a function can declare that a parameter is constexpr (along with overloading rules to allow you to have a non- constexpr version). 基本上,你想要的是一个函数可以声明一个参数是constexpr (以及重载规则,以允许你有一个非constexpr版本)。 No such proposal has been voted into C++20. 没有这样的提案被投票到C ++ 20中。 The best that's been done is P1045 , which hasn't even been discussed by the committee yet, and has a bunch of holes that need to be filled in. So I wouldn't hold my breath. 最好的是P1045 ,委员会尚未讨论过,并且还有一堆需要填补的漏洞。所以我不会屏住呼吸。

Try this one. 试试这个吧。 It's example that is quite simply scalable: 这个例子非常简单:

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);
}

It shows compile time error :). 它显示编译时错误:)。 I used this SO question as guideline: constexpr constructor with compile time validation 我使用这个SO问题作为指导: constexpr构造函数与编译时验证

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM