g ++不会使用assert编译constexpr函数

时间:2013-09-06 00:32:31

标签: c++ c++11 g++ assert constexpr

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&amp;,const T&amp;,const T&amp;)[with T = long unsigned int]'< strong>不是退货声明

使用g++ 4.8.1clang++ 3.4不会抱怨。

谁在这?我可以用g++编译代码而不使用宏吗?

4 个答案:

答案 0 :(得分:13)

海湾合作委员会是对的。但是,有一个相对简单的解决方法:

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

我们滥用逗号运算符两次。

第一次因为我们想要asserttrue可以从constexpr函数调用constexpr。第二个,所以我们可以将两个函数链接成一个constexpr_assert函数。

另一个好处是,如果在编译时无法将true表达式验证为getClamped,则constexpr函数不是assert_helper

assert存在是因为NDEBUG的内容是constexpr_assert为真时的实现定义,因此我们无法将其嵌入到表达式中(它可以是语句,而不是表达式) 。即使constexprassert(例如,当constexpr为假时),它也可以保证失败的NDEBUG无法{{1}}。

所有这一切的缺点是你的断言不是在问题发生的那一行发射,而是2次更深的呼叫。

答案 1 :(得分:5)

从C ++ 14开始,这不再是一个问题;带有g++标志的-std=c++14可以编译并运行您的代码。

有三个缺点:

  • 如您的问题中所述,在C ++ 11中无效
  • 当然,assert永远不会在编译时被触发。即使添加具有相同条件的static_assert也不会有效,因为mMinmMax不被视为常量表达式。
  • 此外,因为assert在编译时没有被触发,但是函数是constexpr,如果条件是 false 但是表达式在编译时进行评估(例如constexpr auto foo = getClamped(1,2,0);),assert从不触发 - 意味着不会捕获不正确的函数参数。

在评论中,用户oliora链接到interesting blog post by Eric Niebler,该variation of this approach描述了在C ++ 11中工作的多种方法可以在编译时或在运行时适当时触发。

简而言之,策略是:

  • throw例外;要使其无法捕获(即更像assert),请标记constexpr函数nothrow
    • Niebler并没有在他的帖子中调用它,但throw表达式必须包含在评估的某种更大的逻辑表达式中assert ed的条件是false,例如三元表达式(这是Niebler在他的例子中使用的)。即使在C ++ 14中,也允许单独的if (condition) throw <exception>;语句
    • Niebler也没有注意到,与assert不同,此方法依赖于NDEBUG;发布版本触发失败并崩溃。
  • 抛出自定义表达式类型,其构造函数调用std::quick_exit。这消除了对nothrow的需求。
    • 同样,这不会被编译出来用于发布版本(除非你将quick_exit电话打包在ifdef中)。
  • 在lambda中包装一个实际的assert,该lambda传递给一个采用任意可调用的结构(作为模板参数)并调用它,然后调用std::quick_exit,然后调用{{1}那个结构。这个似乎是严重的过度杀伤,但当然它会在运行时生成一个真正的断言失败消息,这很好。
    • 这是不会导致版本构建崩溃的唯一方法。
    • oliora提供的mysqli_real_escape_string()没有throwthrow。这似乎更清洁,更健全。

答案 2 :(得分:3)

constexpr在编译时计算。非静态断言在运行时。

答案 3 :(得分:1)

g ++是对的。根据标准,assert语句中不允许使用非静态constexpr

  

...它的函数体应该是一个只包含的复合语句:
   空陈述,
   static_assert申述,
   typedef声明和不定义类或枚举的别名声明,
   使用申述,
   using指令,
   并且只有一个回报声明     - 7.1.5 / 3