叮当声和海湾合作委员会符合未使用的变量的资格

发布于 2025-01-26 14:13:47 字数 1502 浏览 4 评论 0原文

我在公关评论中注意到一个未使用的变量,我们想知道为什么编译器没有抓住这一点。因此,我用Godbolt进行了以下代码测试,其中有很多未使用的变量,并感到惊讶的是,有些人被报道为未使用,而另一些则没有。即使所有人都没有使用。

#include <string>

struct Index
{
  Index(int index) : m_index(index) {}
  int m_index;
};

int main()
{
    std::string str = "hello"; // case 1. no warning here - unexpected
    int someValue = 2; // case 2. warning - as expected
    const int someConstant = 2; // case 3. warning - as expected
    Index index1(2);  // case 4. just as equally not used but no warning - unexpected
    // here using the assignment but do get a warning here
    // but the str assignment doesn't give a warning - weird
    Index index2 = 2; // case 5.
    Index index3{2}; // case 6. just as equally not used but no warning - unexpected
    Index index4 = {2}; // case 7. just as equally not used but no warning - unexpected
    return 0;
}

警告:未使用的变量'someValue'[-wunused-variable]

警告:未使用的变量'index2'[-wunused-variable](仅在clang上警告,而不是在GCC上警告)

警告:未使用的变量'someContant'[-wunused-variable]

,那么clang和GCC符合未使用的条件?如果我使用锁怎么办?我声明了它,但不要直接使用它,而是将其用于自动释放资源。如果有一天开始发出有关锁的警告,我该如何告诉编译器我正在使用它?

int g_i = 0;
std::mutex g_i_mutex;  // protects g_i
 
void safe_increment()
{
    const std::lock_guard<std::mutex> lock(g_i_mutex);
    ++g_i;
    // g_i_mutex is automatically released when lock goes out of scope
}

标志: - 变化 叮当:14.0.0 GCC:11.3

I noticed in a PR review an unused variable and we were wondering why compiler didn't catch that. So I tested with godbolt the following code with bunch of unused variables and was surprised that some were reported as unused but others not. Even though all of them are unused.

#include <string>

struct Index
{
  Index(int index) : m_index(index) {}
  int m_index;
};

int main()
{
    std::string str = "hello"; // case 1. no warning here - unexpected
    int someValue = 2; // case 2. warning - as expected
    const int someConstant = 2; // case 3. warning - as expected
    Index index1(2);  // case 4. just as equally not used but no warning - unexpected
    // here using the assignment but do get a warning here
    // but the str assignment doesn't give a warning - weird
    Index index2 = 2; // case 5.
    Index index3{2}; // case 6. just as equally not used but no warning - unexpected
    Index index4 = {2}; // case 7. just as equally not used but no warning - unexpected
    return 0;
}

warning: unused variable 'someValue' [-Wunused-variable]

warning: unused variable 'index2' [-Wunused-variable] (warning only on clang, not on gcc)

warning: unused variable 'someConstant' [-Wunused-variable]

So what do clang and gcc qualify as unused? What if I'm using a lock? I declare it but don't use it directly but use it for automatic releasing of a resource. How do I tell the compiler that I am using it if one day it starts to give a warning about the lock?

int g_i = 0;
std::mutex g_i_mutex;  // protects g_i
 
void safe_increment()
{
    const std::lock_guard<std::mutex> lock(g_i_mutex);
    ++g_i;
    // g_i_mutex is automatically released when lock goes out of scope
}

flags: -Wunused-variable
clang: 14.0.0
gcc: 11.3

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

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

发布评论

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

评论(1

烏雲後面有陽光 2025-02-02 14:13:47

没有警告的原因是,当您初始化它们时,非平凡类型类型的变量在技术上并没有使用,而是在功能中切勿访问它们。

考虑一下此示例

struct Trivial {};

struct NonTrivial {
    NonTrivial() {
        //Whatever
    }
};

void test() {
    Trivial t;
    NonTrivial nt;
}

GCC警告琐事T;自从此声明以来是未使用的切勿导致任何用户定义的代码运行;唯一的运行是无笨拙的琐碎的构造函数和微不足道的破坏者。因此,根本没有在琐碎的t上执行任何操作,并且它确实没有使用(它的内存甚至从未触摸)。

非平凡的NT;不会引起警告,但是,实际上它是使用使用运行其构造函数的,即用户定义的代码。

这也是为什么编译器不会警告“未使用的锁定后卫”或类似的RAII类的原因 - 它们用于在施工和破坏时运行用户定义的代码,这意味着它们使用了(a)指向对象的指针传递给用户定义的构造函数/destructor = tode =使用=使用)。

这可以通过gnu :: pure属性:

struct Trivial {};

struct NonTrivial {
    [[gnu::pure]] NonTrivial() {
        //Whatever
    }
};

void test() {
    Trivial t;
    NonTrivial nt;
}

在这种情况下,GCC警告他们两个非平地是一个no-op,向我们发出了“未使用的变量”警告。 (它还警告gnu :: pure在void函数上使用,这很公平。您通常不应该这样做。)

Clang对以下代码的警告也很有意义。

struct Hmm {
  int m_i;
  Hmm(int i): m_i(i) {}
};

void test() {
  Hmm hmm = 2; //Case 5 from the question
}

到以下:

void test() {
  Hmm hmm = Hmm(2);
}

这是等效 具有副作用(称为用户定义的构造函数),因此该临时性并非未使用。但是,然后将临时性移至本地变量hmm hmm。移动构造函数和该本地变量的攻击函数都是微不足道的(因此不调用用户代码),因此该变量确实是未使用的,因为编译器可以证明程序的行为是否相同,无论该变量是否相同存在(如上所述,Trivial CTOR + Trivial DTOR +无其他访问变量=未使用的变量)。如果hmm具有非平凡的移动构造函数或非平凡的破坏者,则不会使用。

请注意,一个琐碎的移动构造函数使移动的对象完整保持完整,因此它确实没有任何副作用(除了初始化正在构造的对象之外)。

可以通过删除MOVE构造器来轻松验证,该构建器导致clang and gcc和gcc conflang and gcc compland

The reason why there's no warning is that variables of non-trivial class type aren't technically unused when you initialize them but then never access them in your function.

Consider this example:

struct Trivial {};

struct NonTrivial {
    NonTrivial() {
        //Whatever
    }
};

void test() {
    Trivial t;
    NonTrivial nt;
}

GCC warns about Trivial t; being unused since this declaration never causes any user-defined code to run; the only thing that's run are the trivial constructor and trivial destructor, which are no-ops. So no operation at all is performed on Trivial t and it is truly unused (its memory is never even touched).

NonTrivial nt; doesn't cause a warning, however, since it is in fact used to run its constructor, which is user-defined code.

That's also why compilers are not going to warn about "unused lock guards" or similar RAII classes - they're used to run user-defined code at construction and destruction, which means that they are used (a pointer to the object is passed to the user-defined constructor/destructor = address taken = used).

This can further be proved by marking the object's constructor with the gnu::pure attribute:

struct Trivial {};

struct NonTrivial {
    [[gnu::pure]] NonTrivial() {
        //Whatever
    }
};

void test() {
    Trivial t;
    NonTrivial nt;
}

In this case, GCC warns about both of them because it knows that NonTrivial::NonTrivial() doesn't have side-effects, which in turn enables the compiler to prove that construction and destruction of a NonTrivial is a no-op, giving us back our "unused variable" warning. (It also warns about gnu::pure being used on a void function, which is fair enough. You shouldn't usually do that.)

Clang's warning about the following code also does make sense.

struct Hmm {
  int m_i;
  Hmm(int i): m_i(i) {}
};

void test() {
  Hmm hmm = 2; //Case 5 from the question
}

This is equivalent to the following:

void test() {
  Hmm hmm = Hmm(2);
}

Construction of the temporary Hmm(2) has side-effects (it calls the user-defined constructor), so this temporary is not unused. However, the temporary then gets moved into the local variable Hmm hmm. Both the move constructor and the destructor of that local variable are trivial (and therefore don't invoke user code), so the variable is indeed unused since the compiler can prove that the behavior of the program would be the same whether or not that variable is present (trivial ctor + trivial dtor + no other access to the variable = unused variable, as explained above). It wouldn't be unused if Hmm had a non-trivial move constructor or a non-trivial destructor.

Note that a trivial move constructor leaves the moved-from object intact, so it truly does not have any side-effects (other than initializing the object that's being constructed).

This can easily be verified by deleting the move constructor, which causes both Clang and GCC to complain.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文