C ++语言模板问题

时间:2010-01-08 02:51:36

标签: 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
}

如下所述,g ++ v3.4.6编译器抱怨:

test.cc: In constructor `test::test()':
test.cc:11: error: invalid initialization of reference of type 'const volatile bool&' from expression of type 'volatile bool'
test.cc:3: error: in passing argument 1 of `void unused(const T&) [with T = volatile bool]'

此处的目标是使用未使用的抑制优化代码中出现的未使用变量警告。我有一个宏进行断言检查,在优化代码中断言消失了,但我希望断言表达式中的任何变量都保持引用,这样我才能在优化代码中得到未使用的变量警告。在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. */
}

然而,当参数可以被引用时,其他调用unused()的情况由于模糊而失败:

file.h:176: error: call of overloaded `unused(bool)' is ambiguous
myAssert.h:27: note: candidates are: void unused(T) [with T = bool]
myAssert.h:34: note:                 void unused(const T&) [with T = bool]

所以我的问题是,如何更改unused()或重载它以使其满足以下要求:

  1. 编译器可以将对unused()的调用优化为no-op。
  2. 它会导致传递给unused()的表达式中出现的任何变量出现使用,因此不会导致有关它们被定义但未被使用的警告。
  3. unused()的参数可能会被引用,也可能不会被引用。
  4. unused()的参数可能是一个带有昂贵的复制构造函数的对象,在调用unused()时不应该调用它。

感谢。

- 威廉

10 个答案:

答案 0 :(得分:3)

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

(void) x;

其中x是其他未引用的值。

答案 1 :(得分:2)

Charles Nicholson建议做这样的事情,以便在this article中解释的原因标记未使用的变量:

#define UNSUSED(a) ((void)sizeof(a))

短版本是... sizeof不评估表达式,但编译器在此上下文中看到它仍然将其视为“已使用”。

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

答案 2 :(得分:1)

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

#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_ENABLED,否则不会产生代码,也不会产生有关未使用变量的警告。这几乎就是libc中assert宏的工作方式。

我认为问题在于变量仅用于断言,这是一种做你想做的不好的方法。为什么不将它标记为未使用并使用断言宏单独,这样很明显该变量并未真正用于任何事情,但您仍然可以获得调试构建断言。只是单独解决问题。

答案 3 :(得分:1)

更改未使用的定义:

inline void unused(bool) {}

由于您已经需要一个需要转换为bool的表达式,因此无需进行该转换。 Inline允许编译器进行优化,包括在表达式没有副作用的情况下(但是你必须测试以确切知道在复杂情况下会发生什么)。

此外,这解决了大多数断言宏的常见问题:如果表达式 具有副作用,那么将始终对其进行评估。 (根据用途,可能非常好或非常糟糕。)

答案 4 :(得分:1)

正如约翰内斯在评论中所说,你遇到了编译错误。您可以通过明确转换为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. */
}

另外,很高兴你把const放在它所属的类型之后。

答案 5 :(得分:1)

编译器警告与已使用或未使用无关。您正在将一个volatile变量 - readWriteActivated - 传递给不接受volatile引用的函数。尝试一个const cast。

答案 6 :(得分:0)

为什么不完全跳过模板并使用省略号。

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

答案 7 :(得分:0)

如果要取消未使用的变量警告,为什么要将其称为
未使用(readWriteActivated!); ?为什么你不能把它称为为什么 未使用(readWriteActivated); 并将代码设为

template<typename T>
void UnUsed(const T& )
{

}

有关更多参考资料,请参阅Herb Sutter Here

的博客文章

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

答案 8 :(得分:0)

这是一个错误:

http://gcc.gnu.org/bugzilla/show_bug.cgi?id=42655

在unused()级别没有解决方法。相反,每次出现都可以通过在调用unused():

之前引入临时变量来解决
template<typename T>
void
unused(T const &) {
  /* Do nothing. */
}

int main() {
  volatile bool x = false;
  bool avoidGCC42655 = !x; // type of "!x" is bool
  unused(avoidGCC42655);
}

答案 9 :(得分:-1)

如果表达式的值无关紧要,为什么要传递!readWriteActivated而不是readWriteActivated