C++ C++;语言模板问题

C++ C++;语言模板问题,c++,templates,C++,Templates,下面是一个小测试用例,演示了我试图用C++中的模板解决的一个问题: template<typename T> void unused(T const &) { /* Do nothing. */ } int main() { volatile bool x = false; unused(!x); // type of "!x" is bool } 这里的目标是让未使用的禁止在优化代码中出现未使用的变量警告。我有一个宏,它执行断言检查,在优化代码中断言消失,但

下面是一个小测试用例,演示了我试图用C++中的模板解决的一个问题:

template<typename T>
void
unused(T const &) {
  /* Do nothing. */
}

int main() {
  volatile bool x = false;
  unused(!x); // type of "!x" is bool
}
这里的目标是让未使用的禁止在优化代码中出现未使用的变量警告。我有一个宏,它执行断言检查,在优化代码中断言消失,但我希望断言表达式中的任何变量都保持引用状态,这样我就不会只在优化代码中收到未使用的变量警告。在unused()模板函数的定义中,我使用了一个引用,这样就不会无意中运行复制构造函数代码,这样编译器就可以完全省略对unused的调用

对于感兴趣的人,断言宏如下所示:

#ifdef NDEBUG
#  define Assert(expression) unused(expression)
#else // not NDEBUG
#  define Assert(expression)      \
{         \
  bool test = (expression);      \
     \
  if (!test) {        \
    if (StopHere(__LINE__, __FILE__, __PRETTY_FUNCTION__,  \
                    #expression, false)) {    \
      throw Exit(-1); /* So that destructors are run. */  \
    }         \
  }         \
}
#endif // else not NDEBUG
template<typename T>
void
unused(T const) {
  /* Do nothing. */
}
#define UNUSED(x) ((void)x)
#if defined ASSERT_ENABLED
#define TEST(test) (!(test)) ? assert_failed(# test, __FILE__, __LINE__) : (void)0    
#else
#define TEST(ignore) ((void)0)
#endif
对于上述测试用例,我可以通过添加另一个类似的未使用函数来消除错误,如下所示:

#ifdef NDEBUG
#  define Assert(expression) unused(expression)
#else // not NDEBUG
#  define Assert(expression)      \
{         \
  bool test = (expression);      \
     \
  if (!test) {        \
    if (StopHere(__LINE__, __FILE__, __PRETTY_FUNCTION__,  \
                    #expression, false)) {    \
      throw Exit(-1); /* So that destructors are run. */  \
    }         \
  }         \
}
#endif // else not NDEBUG
template<typename T>
void
unused(T const) {
  /* Do nothing. */
}
#define UNUSED(x) ((void)x)
#if defined ASSERT_ENABLED
#define TEST(test) (!(test)) ? assert_failed(# test, __FILE__, __LINE__) : (void)0    
#else
#define TEST(ignore) ((void)0)
#endif
所以我的问题是,如何更改unused()或重载它,使其满足以下要求:

  • 编译器可以将对unused()的调用优化为no-op。
  • 它会导致传递给unused()的表达式中存在的任何变量显示为已使用,因此不会导致关于这些变量已定义但未使用的警告。
  • unused()的参数可能无法引用,也可能无法引用。
  • unused()的参数可能是具有昂贵副本构造函数的对象,在调用unused()时不应调用该构造函数。 谢谢


    -William

    为什么不完全跳过模板,使用省略号呢

    inline void unused (...) { /* do nothing */ }
    

    你为什么要通过
    !如果表达式的值无关紧要,则readWriteActivated
    而不是
    readWriteActivated

    实现此目的的一种常见(且更简单)方法是将结果强制转换为
    void

    (void) x;
    

    其中x是一些未引用的值。

    如果要抑制未使用变量警告,为什么将其称为
    未使用(!readWriteActivated)?你为什么不能叫它
    未使用(读写激活); 并将代码设置为

    template<typename T>
    void UnUsed(const T& )
    {
    
    }
    
    模板
    无效未使用(常数T&)
    {
    }
    
    如需更多参考资料,请参阅Herb Sutter的博客文章

    编辑:删除函数中的参数名称。这适用于
    未使用(!readWriteActivated);还有。

    我见过的最好的解决方案是:

    #ifdef NDEBUG
    #  define Assert(expression) unused(expression)
    #else // not NDEBUG
    #  define Assert(expression)      \
    {         \
      bool test = (expression);      \
         \
      if (!test) {        \
        if (StopHere(__LINE__, __FILE__, __PRETTY_FUNCTION__,  \
                        #expression, false)) {    \
          throw Exit(-1); /* So that destructors are run. */  \
        }         \
      }         \
    }
    #endif // else not NDEBUG
    
    template<typename T>
    void
    unused(T const) {
      /* Do nothing. */
    }
    
    #define UNUSED(x) ((void)x)
    
    #if defined ASSERT_ENABLED
    #define TEST(test) (!(test)) ? assert_failed(# test, __FILE__, __LINE__) : (void)0    
    #else
    #define TEST(ignore) ((void)0)
    #endif
    
    它是可移植的,并成功抑制警告

    编辑

    既然您已经说过这更像是一个断言,那么您可能应该这样做:

    #ifdef NDEBUG
    #  define Assert(expression) unused(expression)
    #else // not NDEBUG
    #  define Assert(expression)      \
    {         \
      bool test = (expression);      \
         \
      if (!test) {        \
        if (StopHere(__LINE__, __FILE__, __PRETTY_FUNCTION__,  \
                        #expression, false)) {    \
          throw Exit(-1); /* So that destructors are run. */  \
        }         \
      }         \
    }
    #endif // else not NDEBUG
    
    template<typename T>
    void
    unused(T const) {
      /* Do nothing. */
    }
    
    #define UNUSED(x) ((void)x)
    
    #if defined ASSERT_ENABLED
    #define TEST(test) (!(test)) ? assert_failed(# test, __FILE__, __LINE__) : (void)0    
    #else
    #define TEST(ignore) ((void)0)
    #endif
    
    除非定义了
    ASSERT\u ENABLED
    ,否则不会生成代码,并且不会生成有关未使用变量的警告。libc中的
    assert
    宏就是这样工作的


    我想问题在于,变量只在断言中使用,这是一种很糟糕的方法,无法满足您的需要。为什么不将其标记为未使用,并单独使用断言宏,这样很明显,该变量实际上没有用于任何用途,但您仍然可以获得调试生成断言。只需单独解决问题。

    更改未使用的定义:

    inline void unused(bool) {}
    
    因为您已经需要一个需要转换为bool的表达式,所以这将进行转换,而不进行其他任何操作。内联允许编译器进行优化,包括在表达式没有副作用的情况下(但您必须进行测试才能确切知道在复杂情况下会发生什么)


    此外,这修复了大多数断言宏的一个常见问题:如果表达式确实有副作用,则将始终对其进行计算。(这取决于使用情况,可能非常好,也可能非常坏。)

    查尔斯·尼科尔森建议这样做来标记未使用的变量,原因如下:

    简短的版本是。。。sizeof不会对表达式求值,但在本上下文中看到该表达式时,编译器仍将其视为“已使用”


    我相信这满足了您的所有4个条件,特别是因为sizeof()可以接受任何有效的表达式,并且因为表达式不会被计算(因此不会生成任何代码)。

    正如Johannes在评论中所说,您遇到了编译器错误。您可以通过显式转换为
    bool
    来解决此问题:

    unused( bool( !readWriteActivated) ); // add bool() to any (!volatile_bool_var)
    

    旧答案(但仍然不是一个坏主意) 如果我回忆一下const volatile限定规则,那么您所需要的就是进一步限定虚拟变量。本质上,您只需要在声明的类型vP中重复错误消息

    template<typename T>
    void
    unused(T const volatile &) { // only change is to add "volatile"
      /* Do nothing. */
    }
    
    模板
    无效的
    未使用(T const volatile&){//唯一的更改是添加“volatile”
    /*什么也不做*/
    }
    

    另外,很高兴您将
    常量
    放在类型之后,即它所属的位置。

    编译器警告与已使用或未使用无关。您正在向不接受volatile引用的函数传递volatile变量readWriteActivated。尝试常量转换。

    这是一个错误:

    在unused()级别没有解决方法。相反,可以通过在调用unused()之前引入一个临时变量来解决每次出现的问题:

    模板
    无效的
    未使用(T常数&){
    /*什么也不做*/
    }
    int main(){
    挥发性bool x=假;
    bool avoidGCC42655=!x;//类型为bool的“!x”
    未使用(避免GCC42655);
    }
    
    拒绝第一个代码段看起来像是GCC错误:类型为
    !一些易失性bool
    bool
    而不是
    volatile bool
    ——因此
    T
    应该被推断为
    bool
    ,而不是
    volatile bool
    。请注意,非类类型的rvalue从来都不是const/volatile限定的。报告说:对我来说,这看起来像是Herb Sutter在他的博客中提到的重复,顺便说一句,你在使用哪个编译器?可能是个bug,但如果他没有调用
    unused(readWriteActivated)
    ,那听起来就像他的意图一样。g++v3.4.6,但是这些版本中的错误也很明显:4.0.0 4.1.0 4.2.0 4.3.0 4.4.0 4.5.0然后在前面抛出一个
    &
    。可变函数必须具有