有没有一个好的方法来确保 C++函数结果不被忽略?

发布于 2024-11-17 01:31:46 字数 1165 浏览 3 评论 0原文

我最近遇到一个案例,我有一个 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<>仅用于调试,不可用于发布。

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

I ran into a case recently where I had a const member function performing an operation and returning a result. For example,

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

But someone else was inadvertently calling it like it was updating the this object (ignoring the result):

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

(This bug was actually introduced by an imperfect refactoring.)

Is there a way to make the last line above trigger an error or a warning? The next best thing would be a run-time catch, which is mostly addressed by the following template. However, it kills the return value optimization, as seen by the counter result.

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;
}

I suppose I can ameliorate the inefficiency by using a macro so that MustTake<> is debug only and a no-op for release.

I am looking for a compile-time solution. Failing that, I am looking for the best run-time solution.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

你的心境我的脸 2024-11-24 01:31:46

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

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

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

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

This is what function attributes are for (documentation) in GCC and Clang, but it's not portable to e.g. MSVC.

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

The documentation says it's used on realloc for example, but it doesn't appear on any other standard functions on my system.

You may also be interested in using the Clang static analyzer, which tracks data flow across function calls and can give you better warnings.

虚拟世界 2024-11-24 01:31:46

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

For Microsoft VC++, there is the _Check_return_ annotation: http://msdn.microsoft.com/en-us/library/ms235402(v=VS.100).aspx

濫情▎り 2024-11-24 01:31:46

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

BEFORE:

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

AFTER:

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;
    }
};

If it is important that a return value not be ignored by the caller of a function, the template dont_ignore may be used as follows

BEFORE:

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

AFTER:

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

When the caller of the function does not use the return value, an exception is thrown. Definition of 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;
    }
};
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文