【问题标题】:Is there a good way to make sure a C++ function result is not ignored?有没有一种好方法可以确保不忽略 C++ 函数结果?
【发布时间】:2011-09-19 23:20:21
【问题描述】:

我最近遇到了一个案例,我有一个 const 成员函数执行操作并返回结果。例如,

class Foo { ...
    Foo add(Foo const & x) const;
}

但其他人无意中调用它,就像它正在更新 this 对象(忽略结果):

Foo a = ...;
Foo b = ...;
a.add(b);

(这个错误实际上是由不完美的重构引入的。)

有没有办法让上面的最后一行触发错误或警告?下一个最好的事情是运行时捕获,这主要由以下模板解决。但是,它会终止返回值优化,如计数器结果所示。

template<typename T>
class MustTake {
    T & obj;
    bool took;
public:
    MustTake(T o) : obj(o), took(false) {}
    ~MustTake() { if (!took) throw "not taken"; }
    operator T&() { took = true; return obj;}
};

struct Counter {
    int n;
    Counter() : n(0) {}
    Counter(Counter const & c) : n(c.n+1) {}
    ~Counter() {}
};

Counter zero1() {
    return Counter();
}

MustTake<Counter> zero2() {
    return Counter();
}

int main() {
    Counter c1 = zero1();
    printf("%d\n",c1.n);    // prints 0
    Counter c2 = zero2();
    printf("%d\n",c2.n);    // prints 1
    zero1();    // result ignored
    zero2();    // throws
    return 0;
}

我想我可以通过使用宏来改善效率低下的问题,这样 MustTake 只能用于调试,而不能用于发布。

我正在寻找编译时解决方案。如果做不到这一点,我正在寻找最佳的运行时解决方案。

【问题讨论】:

  • 如果您使用的是 GCC,我认为它有 -Wunused-result 标志来启用警告。
  • 我的建议是选择一个更好的方法名称。如果我读到a.add(b),我会立即想到mutator。 a.newListWithHead(b) 或类似的东西怎么样,取决于实际发生的情况?不像add 那样简洁,但添加并不是你在这里真正要做的。
  • 我喜欢 spong 改名的想法。例如,您可以将其更改为a + b(如果这样做有意义的话)然后每个人都会知道“a + b”不会修改a,但他们可以很容易地写成“a += b”来获得他们想要的效果(我认为)。
  • @Sam 和其他人,我正在使用 MSVC 2010 和 GCC 3(我想这将是带有 Xcode 4 的 Clang)。
  • -Wunused-result 倾向于喷出。用户不会在 spew 中看到关于 a.add(b) 的警告。

标签: c++ lint


【解决方案1】:

这是 GCC 和 Clang 中 (documentation) 的函数属性,但它不能移植到例如MSVC。

class Foo { ...
    __attribute__((warn_unused_result))
    Foo add(Foo const & x) const;
}

文档说它用于realloc例如,,但它没有出现在我系统上的任何其他标准功能上。

您可能还对使用 Clang 静态分析器感兴趣,该分析器可以跟踪函数调用之间的数据流,并可以为您提供更好的警告。

【讨论】:

  • 它在我的系统 (debian sid) 上的很多系统调用中使用 - 例如,read()write()。在 glibc 中是 #defined 到 __wur(当然这是 C 库的实现细节)
  • 呵呵,grep 没听懂,因为没有找到__wur 的定义。
  • 您可以通过将其包装在一个检查编译器是 GCC 还是 clang 的宏中来“使其可移植”。对于 MSVC,可以使用编译器标志 /W3 来启用有关未使用变量的警告。 See here。虽然这使它适用于所有次。
  • @rwols:不幸的是未使用的变量!=未使用的结果。你也可以#define __attribute__().
  • @DietrichEpp 你完全正确,我的错。啊,我什至没有see this answer
【解决方案2】:

对于 Microsoft VC++,有 _Check_return_ 注释:http://msdn.microsoft.com/en-us/library/ms235402(v=VS.100).aspx

【讨论】:

【解决方案3】:

如果函数调用者不忽略返回值很重要,模板dont_ignore可以如下使用

之前:

int add( int x, int y )
{
    return x + y;
}

之后:

dont_ignore<int> add( int x, int y )
{
    return x + y;
}

当函数的调用者不使用返回值时,会抛出异常。 dont_ignore的定义:

template<class T>
struct dont_ignore
{
    const T     v;
    bool        used;

    dont_ignore( const T& v )
        :  v( v ), used( false )
    {}

    ~dont_ignore()
    {
        if ( !used )
            throw std::runtime_error( "return value not used" );
    }

    operator T()
    {
        used = true;
        return v;
    }
};

【讨论】:

猜你喜欢
  • 2010-09-17
  • 1970-01-01
  • 2012-08-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-05-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多