constexpr,static_assert和inlining

Dav*_*one 13 c++ inline static-assert constexpr c++11

我之前根据参数是否constexpr询问函数重载.我正试图解决这个问题的令人失望的答案,以建立一个更聪明的断言功能.这大致是我想做的事情:

inline void smart_assert (bool condition) {
    if (is_constexpr (condition))
        static_assert (condition, "Error!!!");
    else
        assert (condition);
}
Run Code Online (Sandbox Code Playgroud)

基本上,我们的想法是编译时检查总是比运行时检查更好,如果可以在编译时检查.但是,由于内联和常量折叠之类的东西,我不能总是知道是否可以进行编译时间检查.这意味着可能存在assert (condition)编译的情况,assert(false)代码只是等待我运行它并在我发现错误之前执行该路径.

因此,如果有某种方法来检查条件是否是constexpr(由于内联或其他优化),我可以static_assert在可能的情况下调用,否则返回运行时断言.幸运的是,gcc具有内在函数__builtin_constant_p (exp),如果exp是constexpr 则返回true .我不知道其他编译器是否有这种内在的,但我希望这可以解决我的问题.这是我提出的代码:

#include <cassert>
#undef IS_CONSTEXPR

#if defined __GNUC__
    #define IS_CONSTEXPR(exp) __builtin_constant_p (exp)
#else
    #define IS_CONSTEXPR(exp) false
#endif
// TODO: Add other compilers

inline void smart_assert (bool const condition) { 
    static_assert (!IS_CONSTEXPR(condition) or condition, "Error!!!");
    if (!IS_CONSTEXPR(condition))
        assert (condition);
}

#undef IS_CONSTEXPR
Run Code Online (Sandbox Code Playgroud)

static_assert依靠的短路行为or.如果IS_CONSTEXPR为真,则static_assert可以使用,条件是!true or condition,与刚刚相同condition.如果IS_CONSTEXPR为false,则static_assert不能使用,条件是!false or condition,它是相同的,true并且static_assert被忽略.如果static_assert因为condition不是constexpr 而无法检查,那么我assert在我的代码中添加一个运行时作为最后的努力.但是,由于无法在a中使用函数参数static_assert,即使参数是constexpr,不起作用.

特别是,如果我尝试使用gcc编译,会发生这种情况:

// main.cpp
int main () {
    smart_assert (false);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

g++ main.cpp -std=c++0x -O0

一切都很好,正常编译.没有内联没有优化,所以IS_CONSTEXPR是false并且static_assert被忽略,所以我得到一个运行时assert语句(失败).然而,

[david@david-desktop test]$ g++ main.cpp -std=c++0x -O1
In file included from main.cpp:1:0:
smart_assert.hpp: In function ‘void smart_assert(bool)’:
smart_assert.hpp:12:3: error: non-constant condition for static assertion
smart_assert.hpp:12:3: error: ‘condition’ is not a constant expression
Run Code Online (Sandbox Code Playgroud)

一旦我打开任何优化并因此可能允许static_assert被触发,它就会失败,因为我不能在中使用函数参数static_assert.有没有办法解决这个问题(即使它意味着实现我自己的static_assert)?我觉得我的C++项目理论上可以从一个更聪明的断言声明中获益,它尽可能早地捕获错误.

smart_assert在一般情况下,制作类似函数的宏似乎不会解决问题.它显然会使它在这个简单的例子中起作用,但condition可能来自调用图的两个级别的函数(但constexpr由于内联而仍然被编译器知道),这遇到了使用函数参数的相同问题在一个static_assert.

Joh*_*itb 8

这应该可以帮助你开始

template<typename T> 
constexpr typename remove_reference<T>::type makeprval(T && t) {
  return t;
}

#define isprvalconstexpr(e) noexcept(makeprval(e))
Run Code Online (Sandbox Code Playgroud)

  • 非常好的约翰内斯.它为什么有效?每个constexpr都是左值参考吗?为什么这个名字?什么是公关?做什么呢?VAL?noexcept是否会返回bool?好的,我会查看noexcept,但任何详细说明都会感激不尽.它有一个upvote为你;) (3认同)

Che*_*Alf 6

一般而言,明确是好的,隐含是坏的.

程序员总是可以试试static_assert.

如果在编译时无法评估条件,那么失败,程序员需要更改为assert.

您可以通过提供一个通用表单来更容易地做到这一点,以便更改减少到例如STATIC_ASSERT( x+x == 4 )DYNAMIC_ASSERT( x+x == 4 ),只是重命名.

也就是说,因为在你的情况下你只想优化程序员的时间,如果可用的优化,即因为你可能不关心总是与所有编译器得到相同的结果,你总是可以尝试像...

#include <iostream>
using namespace std;

void foo( void const* ) { cout << "compile time constant" << endl; }
void foo( ... ) { cout << "hm, run time,,," << endl; }

#define CHECK( e ) cout << #e << " is "; foo( long((e)-(e)) )

int main()
{
    int x   = 2134;
    int const y     = 2134;

    CHECK( x );
    CHECK( y );
}
Run Code Online (Sandbox Code Playgroud)

如果你这样做,那么请告诉我们它是如何淘汰的.

注意:上面的代码确实在MSVC 10.0和g ++ 4.6中产生了不同的结果.


更新:我想知道如何评论上面的代码是如何工作的,得到了​​如此多的赞成.我想也许他说的是我根本不懂的东西.所以我开始做OP的工作,检查这个想法的表现.

在这一点上我认为,如果constexpr功能的东西,可以进行与G ++的工作,那么它可能也是解决问题的G ++,否则,只能为其他编译器.

以上是我对g ++支持的支持.使用我提出的想法,这适用于Visual C++,可以很好地解决(解决OP的问题).但不是用g ++:

#include <assert.h>
#include <iostream>
using namespace std;

#ifdef __GNUC__
    namespace detail {
        typedef double (&Yes)[1];
        typedef double (&No)[2];

        template< unsigned n >
        Yes foo( char const (&)[n] );

        No foo( ... );
    }  // namespace detail

    #define CASSERT( e )                                        \
        do {                                                    \
            char a[1 + ((e)-(e))];                              \
            enum { isConstExpr = sizeof( detail::foo( a ) ) == sizeof( detail::Yes ) }; \
            cout << "isConstExpr = " << boolalpha << !!isConstExpr << endl; \
            (void)(isConstExpr? 1/!!(e) : (assert( e ), 0));    \
        } while( false )
#else
    namespace detail {
        struct IsConstExpr
        {
            typedef double (&YesType)[1];
            typedef double (&NoType)[2];

            static YesType check( void const* );
            static NoType check( ... );
        };
    }  // namespace detail

    #define CASSERT( e )                                            \
        do {                                                        \
            enum { isConstExpr =                                    \
                (sizeof( detail::IsConstExpr::check( e - e ) ) ==   \
                    sizeof( detail::IsConstExpr::YesType )) };      \
            (void)(isConstExpr? 1/!!(e) : (assert( e ), 0));        \
        } while( false )
#endif

int main()
{
#if defined( STATIC_TRUE )
    enum { x = true };
    CASSERT( x );
    cout << "This should be displayed, OK." << endl;
#elif defined( STATIC_FALSE )
    enum { x = false };
    CASSERT( x );
    cerr << "!This should not even have compiled." << endl;
#elif defined( DYNAMIC_TRUE )
    bool x = true;
    CASSERT( x );
    cout << "This should be displayed, OK." << endl;
#elif defined( DYNAMIC_FALSE )
    bool x = false;
    CASSERT( x );
    cout << "!Should already have asserted." << endl;
#else
    #error "Hey, u must define a test case symbol."
#endif
}
Run Code Online (Sandbox Code Playgroud)

g ++问题的示例:

[D:\dev\test]
> g++ foo.cpp -Werror=div-by-zero -D DYNAMIC_FALSE

[D:\dev\test]
> a
isConstExpr = true
!Should already have asserted.

[D:\dev\test]
> _

也就是说,g ++报告(甚至通过它的内在函数,甚至是创建VLA的wrt),它知道其值的非const变量是常量,但是它不能将该知识应用于整数除法,这样它就不会产生警告.

哎呀.


更新2:嗯,我是愚蠢的:当然宏可以添加一个普通的assert无论如何.由于OP只对可用时获取静态断言感兴趣,因此在某些极端情况下不适用于g ++.问题解决了,最初解决了.

  • g ++在解析器/语义分析中具有积极的常量折叠,这意味着`(e) - (e)`立即被评估为'0`,因此被认为可用作空指针值...即使在语义上它是一个` int`.然而,Clang不会折叠`x`情况,并且`int`不能隐式转换为指针值,因此`x`不是. (7认同)