如何在 assert() 和 static_assert() 之间调度,取决于是否在 constexpr 上下文中?
How to dispatch between assert() and static_assert(), dependend if in constexpr context?
在 C++11 constexpr 函数中,第二个语句如 assert()
是不可能的。 static_assert()
很好,但如果该函数被称为 'normal' 函数,则将不起作用。逗号运算符可以帮助 wrto。 assert()
,但是很丑陋,一些工具会发出警告。
考虑这样的'getter',它除了断言之外是完全可解释的。但我想为运行时和编译时保留某种断言,但不能仅仅根据 'constexpr' 上下文重载。
template<int Size>
struct Array {
int m_vals[Size];
constexpr const int& getElement( int idx ) const
{
ASSERT( idx < Size ); // a no-go for constexpr funcs in c++11
// not possible, even in constexpr calls as being pointed out, but what I would like:
static_assert( idx < Size, "out-of-bounds" );
return m_vals[idx];
}
};
附加条件:C++11,无堆,无异常,无编译器细节。
注意 正如评论者指出的(谢谢!),static_assert
论点是不可能的(但会很好)。在那种情况下,编译器给了我一个关于越界访问的不同错误。
类似
void assert_impl() { assert(false); } // Replace body with own implementation
#ifdef NDEBUG // Replace with own conditional
#define my_assert(condition) ((void)0)
#else
#define my_assert(condition) ((condition) ? (void()) : (assert_impl(), void()))
#endif
template<int Size>
struct Array {
int m_vals[Size];
constexpr const int& getElement( int idx ) const
{
return my_assert(idx < Size), m_vals[idx];
}
};
如果在上下文需要常量表达式(因为它将调用非constexpr
函数)中使用,它将在断言失败时给出编译时错误。
否则它将在运行时调用 assert
(或您的类似)失败。
据我所知,这是您能做的最好的了。无法使用 idx
的值在编译时强制检查上下文 需要 常量表达式。
逗号运算符语法不好,但 C++11 constexpr
功能非常有限。
当然,正如您已经指出的,如果在需要常量表达式的上下文中使用该函数,无论如何都会诊断出未定义的行为。
如果您知道 assert
(或您的类似物)在条件计算为 true
时不会扩展为常量表达式中禁止的任何内容,但如果条件计算为 false
,那么你可以直接使用它而不是 my_assert
并跳过我在代码中构建的间接寻址。
static_assert
不能在这里使用。 constexpr
函数的参数不允许出现在常量表达式中。因此,在给定的约束条件下您的问题没有解决方案。
但是,我们可以通过弯曲两个约束来解决问题
不使用 static_assert
(使用其他方法来生成编译时诊断),并且
忽略逗号运算符"is ugly and some tools spit warnings about it."(显示其丑陋是C++11 constexpr
函数严格要求的不幸结果)
然后,我们可以使用正常的assert
:
template <int Size>
struct Array {
int m_vals[Size];
constexpr const int& getElement(int idx) const
{
return assert(idx < Size), m_vals[idx];
}
};
在常量评估上下文中,这将发出类似 error: call to non-'constexpr' function 'void __assert_fail(const char*, const char*, unsigned int, const char*)'
的编译器错误。
比逗号表达式更好的是,您可以使用三元条件。第一个操作数是你的断言谓词,第二个操作数是你的成功表达式,并且由于第三个操作数可以是任何表达式——即使是在 C++11 常量上下文中不可用的表达式——你可以使用 lambda 来调用你的库的 ASSERT
设施:
#define ASSERT_EXPR(pred, success) \
((pred) ? \
(success) : \
[&]() -> decltype((success)) \
{ \
ASSERT(false && (pred)); \
struct nxg { nxg() {} } nxg; \
return (success); \
}())
lambda 正文的解释:
ASSERT(false && (pred))
是为了确保使用适当的表达式(用于字符串化)调用您的断言机制。
struct nxg { nxg() {} } nxg
是为了未来安全,以确保如果您使用 NDEBUG
在 C++17 或更高版本中编译,lambda 仍然是非 constexpr
等断言在 const-evaluation 上下文中强制执行。
return (success)
存在有两个原因:确保第二个和第三个操作数具有相同的类型,这样如果您的库尊重 NDEBUG
,则返回 success
表达式不管pred
。 (pred
将被 评估,但您希望断言谓词的评估成本低且没有副作用。)
使用示例:
template<int Size>
struct Array {
int m_vals[Size];
constexpr int getElement( int idx ) const
{
return ASSERT_EXPR(idx < Size, m_vals[idx]);
}
};
constexpr int I = Array<2>{1, 2}.getElement(1); // OK
constexpr int J = Array<2>{1, 2}.getElement(3); // fails
在 C++11 constexpr 函数中,第二个语句如 assert()
是不可能的。 static_assert()
很好,但如果该函数被称为 'normal' 函数,则将不起作用。逗号运算符可以帮助 wrto。 assert()
,但是很丑陋,一些工具会发出警告。
考虑这样的'getter',它除了断言之外是完全可解释的。但我想为运行时和编译时保留某种断言,但不能仅仅根据 'constexpr' 上下文重载。
template<int Size>
struct Array {
int m_vals[Size];
constexpr const int& getElement( int idx ) const
{
ASSERT( idx < Size ); // a no-go for constexpr funcs in c++11
// not possible, even in constexpr calls as being pointed out, but what I would like:
static_assert( idx < Size, "out-of-bounds" );
return m_vals[idx];
}
};
附加条件:C++11,无堆,无异常,无编译器细节。
注意 正如评论者指出的(谢谢!),static_assert
论点是不可能的(但会很好)。在那种情况下,编译器给了我一个关于越界访问的不同错误。
类似
void assert_impl() { assert(false); } // Replace body with own implementation
#ifdef NDEBUG // Replace with own conditional
#define my_assert(condition) ((void)0)
#else
#define my_assert(condition) ((condition) ? (void()) : (assert_impl(), void()))
#endif
template<int Size>
struct Array {
int m_vals[Size];
constexpr const int& getElement( int idx ) const
{
return my_assert(idx < Size), m_vals[idx];
}
};
如果在上下文需要常量表达式(因为它将调用非constexpr
函数)中使用,它将在断言失败时给出编译时错误。
否则它将在运行时调用 assert
(或您的类似)失败。
据我所知,这是您能做的最好的了。无法使用 idx
的值在编译时强制检查上下文 需要 常量表达式。
逗号运算符语法不好,但 C++11 constexpr
功能非常有限。
当然,正如您已经指出的,如果在需要常量表达式的上下文中使用该函数,无论如何都会诊断出未定义的行为。
如果您知道 assert
(或您的类似物)在条件计算为 true
时不会扩展为常量表达式中禁止的任何内容,但如果条件计算为 false
,那么你可以直接使用它而不是 my_assert
并跳过我在代码中构建的间接寻址。
static_assert
不能在这里使用。 constexpr
函数的参数不允许出现在常量表达式中。因此,在给定的约束条件下您的问题没有解决方案。
但是,我们可以通过弯曲两个约束来解决问题
不使用
static_assert
(使用其他方法来生成编译时诊断),并且忽略逗号运算符"is ugly and some tools spit warnings about it."(显示其丑陋是C++11
constexpr
函数严格要求的不幸结果)
然后,我们可以使用正常的assert
:
template <int Size>
struct Array {
int m_vals[Size];
constexpr const int& getElement(int idx) const
{
return assert(idx < Size), m_vals[idx];
}
};
在常量评估上下文中,这将发出类似 error: call to non-'constexpr' function 'void __assert_fail(const char*, const char*, unsigned int, const char*)'
的编译器错误。
比逗号表达式更好的是,您可以使用三元条件。第一个操作数是你的断言谓词,第二个操作数是你的成功表达式,并且由于第三个操作数可以是任何表达式——即使是在 C++11 常量上下文中不可用的表达式——你可以使用 lambda 来调用你的库的 ASSERT
设施:
#define ASSERT_EXPR(pred, success) \
((pred) ? \
(success) : \
[&]() -> decltype((success)) \
{ \
ASSERT(false && (pred)); \
struct nxg { nxg() {} } nxg; \
return (success); \
}())
lambda 正文的解释:
ASSERT(false && (pred))
是为了确保使用适当的表达式(用于字符串化)调用您的断言机制。struct nxg { nxg() {} } nxg
是为了未来安全,以确保如果您使用NDEBUG
在 C++17 或更高版本中编译,lambda 仍然是非constexpr
等断言在 const-evaluation 上下文中强制执行。return (success)
存在有两个原因:确保第二个和第三个操作数具有相同的类型,这样如果您的库尊重NDEBUG
,则返回success
表达式不管pred
。 (pred
将被 评估,但您希望断言谓词的评估成本低且没有副作用。)
使用示例:
template<int Size>
struct Array {
int m_vals[Size];
constexpr int getElement( int idx ) const
{
return ASSERT_EXPR(idx < Size, m_vals[idx]);
}
};
constexpr int I = Array<2>{1, 2}.getElement(1); // OK
constexpr int J = Array<2>{1, 2}.getElement(3); // fails