繁体   English   中英

如果在 constexpr 上下文中,如何在 assert() 和 static_assert()、dependend 之间分派?

[英]How to dispatch between assert() and static_assert(), dependend if in constexpr context?

在 C++11 constexpr 函数中,第二个语句如assert()是不可能的。 static_assert()很好,但如果该函数被称为“正常”函数,则它不起作用。 逗号运算符可以来帮助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函数的参数。 因此,在给定的约束下,您的问题没有解决方案

然而,我们可以通过弯曲两个约束来解决问题

  1. 不使用static_assert (使用其他方法来生成编译时诊断),以及

  2. 忽略逗号运算符“很丑陋,有些工具会发出警告。” (显示其丑陋是 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 评估上下文中强制执行断言。
  • return (success)有两个原因:确保第二个和第三个操作数具有相同的类型,并且如果您的库尊重NDEBUG ,则无论pred是什么,都会返回success表达式。 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()很好,但是如果该函数被称为“正常”函数,则将无法正常工作。 逗号运算符可以来帮助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 (但会很好)。 在那种情况下,编译器给我一个越界访问的错误。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM