假设我想编写一个通用类,它维护一个始终保持在两个值之间的整数。像这样:
template<int Lower, int Upper>
class MyInt {
private:
int m_int;
public:
// Constructors, operators...
};
类不变量是Lower <= m_int <= Upper
。
当然,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-ing around和whatnot,但是我找不到从值语义到模板参数的方法,即使对于这些情况(我声称)这些值显然是编译时常量。
对于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 ++。
答案 0 :(得分:9)
如果我正在寻找的是不可能的,原因是什么?
基本上,它归结为C ++函数模型不允许函数识别constexpr
参数和运行时参数之间的区别这一事实。如果某个函数需要int
,则需要int
。根据常量表达式(如文字)或运行时值是否提供int
,该函数的行为不能有所不同。
即使对于constexpr
函数,这也是如此。您不能拥有可以对其参数进行编译时检查的constexpr
函数。评估可以在编译时完成,但执行foo(500);
最终必须与执行auto b = 500; foo(b);
的行为相同。
这就是为什么变通方法都涉及使用技巧将常量表达式放在模板参数中,这种识别是可能的。当然,模板参数永远不会成为运行时值,因此会产生其他问题(例如必须具有多个函数)。作为一个模板参数是一个通常很痛苦的事情。
基本上,你想要的是一个函数可以声明一个参数是constexpr
(以及重载规则,以允许你有一个非constexpr
版本)。没有这样的提案被投票到C ++ 20中。已经完成的最好的是P1045,委员会尚未对其进行讨论,并且还有一些需要填写的漏洞。所以我不会屏住呼吸。
答案 1 :(得分:6)
试试这个。这个例子非常简单:
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