[英]static_assert doesn't recognize a const char* template parameter as constexpr: g++ bug?
[英]g++ doesn't compile constexpr function with assert in it
template<typename T> constexpr inline
T getClamped(const T& mValue, const T& mMin, const T& mMax)
{
assert(mMin < mMax); // remove this line to successfully compile
return mValue < mMin ? mMin : (mValue > mMax ? mMax : mValue);
}
错误 : constexpr函数的主体 'constexpr T getClamped(const T&,const T&,const T&)[with T = long unsigned int]' not return-statement
使用g++ 4.8.1
。 clang++ 3.4
不抱怨。
谁在这? 我可以用g++
编译代码而不使用宏吗?
GCC是对的。 但是,有一个相对简单的解决方法:
#include "assert.h"
inline void assert_helper( bool test ) {
assert(test);
}
inline constexpr bool constexpr_assert( bool test ) {
return test?true:(assert_helper(test),false);
}
template<typename T> constexpr
inline T getClamped(const T& mValue, const T& mMin, const T& mMax)
{
return constexpr_assert(mMin < mMax), (mValue < mMin ? mMin : (mValue > mMax ? mMax : mValue));
}
我们滥用逗号运算符的地方,两次。
第一次因为我们想要一个assert
,当为true
,可以从constexpr
函数调用。 第二个,所以我们可以将两个函数链接到一个constexpr
函数中。
作为附带好处,如果在编译时无法验证constexpr_assert
表达式为true
,则getClamped
函数不是constexpr
。
assert_helper
存在是因为当NDEBUG
为true时, assert
的内容是实现定义的,所以我们不能将它嵌入到表达式中(它可以是一个语句,而不是一个表达式)。 它还保证即使assert
是constexpr
(例如,当NDEBUG
为false时), constexpr_assert
失败也不能成为constexpr
。
所有这一切的缺点是你的断言不是在发生问题的行发射,而是2次调用更深。
从C ++ 14开始,这不再是一个问题; 带有-std=c++14
标志的g++
编译并运行你的代码就好了。
有三个缺点:
assert
永远不会在编译时触发。 即使添加具有相同条件的static_assert
也不起作用,因为mMin
和mMax
不被视为常量表达式。 assert
未在编译时触发,但功能是constexpr
,如果条件为假 ,但在编译时表达式进行求值(例如constexpr auto foo = getClamped(1,2,0);
),则assert
永远不会触发 - 意味着不会捕获不正确的函数参数。 在评论中,用户oliora链接到Eric Niebler撰写的一篇有趣的博客文章,其中描述了在C ++ 11中有效的多种方法, 并且可以在编译时或在运行时适当时触发。
简而言之,策略是:
throw
异常; 使其无法捕获的(即更像是assert
),标志着constexpr
函数nothrow
throw
表达式必须包含在某种更大的逻辑表达式中, 只有在被assert
的条件为false
才会被计算,例如三元表达式(这是Niebler在他的用法中使用的)例)。 一个独立的if (condition) throw <exception>;
声明将不会被允许,即使在C ++ 14。 assert
不同,这种方法不依赖于NDEBUG
; 发布版本将触发失败和崩溃。 std::quick_exit
。 这消除了nothrow
的需要。
ifdef
包装quick_exit
调用)。 assert
,它传递给一个带有任意可调用的结构(作为模板参数)并调用它,然后调用std::quick_exit
,然后throw
该结构。 这个似乎是严重的过度杀伤,但当然它会在运行时生成一个真正的断言失败消息,这很好。
constexpr在编译时计算。 非静态断言在运行时。
g ++是对的。 根据标准, constexpr
语句中不允许使用非静态assert
。
...其函数体应为仅包含以下内容的复合语句:
空语句,
static_assert申述,
typedef声明和不定义类或枚举的别名声明,
使用申述,
using指令,
而且只有一个退货声明。
- 7.1.5 / 3
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.