我正在尝试确保使用有效数据初始化变量,并希望在编译时执行此检查。这些类有一个static constexpr
方法来检查有效性。在简化版本中,它可能是这样的:
template <typename T>
struct Foo {
constexpr Foo(T initValue) :
mValue(initValue)
{
assert(isValid(iniValue));
}
static constexpr bool isValid(T value) {
return (value > 0);
}
private:
T mValue;
};
现在对于整数类型,我可以使用模板化函数来强制执行编译时检查:
template <typename T, T initVal>
Foo<T> make_new() {
static_assert(Foo<T>::isValid(initVal), "is not valid");
return Foo<T>(initVal);
}
并像这样使用
auto saveInt = make_new<int, 3>();
问题是如何使用非整数类型来做到这一点。我可以做这样的事情,它会触发C ++ 14中的断言:
constexpr Foo<float> constFloat(3.0);
Foo<float> saveFloat = constFloat;
在C ++&gt; = 11中是否有一种更优雅的方式可以让我用单行程做到这一点?最后,我想要分配的值在编译时是已知的,因此该值的有效性也是如此。
答案 0 :(得分:1)
使用当前标准no,您无法在编译时执行此操作。
在Albuquerque ISO C++ Committee Meeting in 2017,对user-defined literals for strings,class/struct types as non-type template parameters,new and delete in constexpr contexts的提案有正面反馈。所以在该标准的未来版本中(如果我们运气好,可能是C ++ 20),它将是可能的。在此之前,当前的限制只允许非类型模板参数的整数类型,并且没有我知道的解决方法。
至于当前限制的原因主要与表示,名称修改以及非整数类型的值的比较有关。
答案 1 :(得分:1)
如果constexpr
检查并返回过滤器,其中包含非consexpr
指令(异常的throw
)错误的情况,如下所示
static constexpr T checkAndRet (T value)
{ return value > 0 ? value
: (throw std::runtime_error("invalid"), T{}); }
您可以在分配前申请该值吗?
constexpr Foo(T initValue) : mValue{checkAndRet(initValue)}
{ }
因此,当您声明constexpr
值
constexpr Foo<float> f2 {-1.0f}; // compile time error
和运行时错误(异常)否则
Foo<float> f3 {-1.0f}; // run time error
以下是一个完整的工作示例,从C ++ 11开始工作
#include <stdexcept>
template <typename T>
struct Foo {
constexpr Foo(T initValue) : mValue{checkAndRet(initValue)}
{ }
static constexpr T checkAndRet (T value)
{ return value > 0 ? value
: (throw std::runtime_error("invalid"), T{}); }
private:
T mValue;
};
int main ()
{
Foo<float> f0 {1.0f}; // OK
constexpr Foo<float> f1 {1.0f}; // OK
//constexpr Foo<float> f2 {-1.0f}; // compile time error
Foo<float> f3 {-1.0f}; // run time error
}
- 编辑 -
OP指定以下
它无法解决我最初的问题。您的上一个代码行仍然只在运行时产生错误,即除非我事先将init值声明为
Foo<float>
,否则无法将constexpr
变量初始化为在编译期间验证的值
抱歉:我部分误解了你的问题。
是;这是可能的,不事先声明constexpr Foo<float>
,但我脑海中的解决方案非常难看,因为它很长并且需要重复元素。
使用C风格的宏,你可以使它简单而优雅,但我认为C风格的宏是邪恶的。
无论如何......如果您在static constexpr
Foo
方法
static constexpr bool isValid (T value)
{ return value > 0 ? true
: (throw std::runtime_error("invalid"), false); }
template <bool>
static constexpr T retV (T value)
{ return value; }
您可以使用constexpr
字面值和编译时检查初始化非Foo<float>
float
对象,如下所示
// OK
Foo<float> f0 { Foo<float>::retV<Foo<float>::isValid(1.0f)>(1.0f) };
// compilation error
// Foo<float> f1 { Foo<float>::retV<Foo<float>::isValid(-1.0f)>(-1.0f) };
正如你所看到的那样非常难看,你必须重复两次这个值,这很容易出错。
但是如果你定义一个C风格的宏如下
#define makeCValue(val) \
Foo<decltype(val)>::retV<Foo<decltype(val)>::isValid(val)>(val)
您可以按如下方式初始化Foo<float>()
个对象
Foo<float> f2 { makeCValue(1.0f) }; // OK
// Foo<float> f3 { makeCValue(-1.0f) }; // compilation error
以下是一个完整的工作示例
#include <stdexcept>
template <typename T>
struct Foo
{
constexpr Foo(T initValue) : mValue{initValue}
{ }
static constexpr bool isValid (T value)
{ return value > 0 ? true
: (throw std::runtime_error("invalid"), false); }
template <bool>
static constexpr T retV (T value)
{ return value; }
T mValue;
};
#define makeCValue(val) \
Foo<decltype(val)>::retV<Foo<decltype(val)>::isValid(val)>(val)
int main ()
{
// OK
Foo<float> f0 { Foo<float>::retV<Foo<float>::isValid(1.0f)>(1.0f) };
// compilation error
// Foo<float> f1 { Foo<float>::retV<Foo<float>::isValid(-1.0f)>(-1.0f) };
Foo<float> f2 { makeCValue(1.0f) }; // OK
// Foo<float> f3 { makeCValue(-1.0f) }; // compilation error
}
显然,您只能将makeCValue()
宏用于编译时已知值(文字值,constexpr
值,...)。