clang 和 gcc 有什么资格作为未使用的变量
What do clang and gcc qualify as variable being unused
我在 PR 审查中注意到一个未使用的变量,我们想知道为什么编译器没有捕捉到它。因此,我使用 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;
}
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]
那么什么是 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
}
标志:-Wunused-variable
铿锵声:14.0.0
海湾合作委员会:11.3
没有警告的原因是 non-trivial class 类型的变量在您初始化它们时在技术上不是未使用的,但是在您的函数中永远不会访问它们。
考虑这个 example:
struct Trivial {};
struct NonTrivial {
NonTrivial() {
//Whatever
}
};
void test() {
Trivial t;
NonTrivial nt;
}
GCC 警告 Trivial t;
未被使用,因为此声明不会导致任何 user-defined 代码变为 运行;唯一 运行 的是平凡的构造函数和平凡的析构函数,它们是 no-ops。所以根本没有对 Trivial t
执行任何操作,它确实未被使用(它的内存从未被触及)。
然而,NonTrivial nt;
不会引起警告,因为它实际上 被 用于 运行 其构造函数,即 user-defined代码。
这也是为什么编译器不会警告“未使用的锁守卫”或类似的 RAII classes - 他们习惯于 运行 user-defined 构造和销毁代码,这意味着它们被使用(指向对象的指针被传递给user-defined constructor/destructor =地址被使用=使用)。
这可以进一步证明 marking the object's constructor with the gnu::pure
attribute:
struct Trivial {};
struct NonTrivial {
[[gnu::pure]] NonTrivial() {
//Whatever
}
};
void test() {
Trivial t;
NonTrivial nt;
}
在这种情况下,GCC 对它们都发出警告,因为它知道 NonTrivial::NonTrivial()
没有 side-effects,这反过来又使编译器能够证明 [= 的构造和破坏19=] 是一个 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
}
这是equivalent以下内容:
void test() {
Hmm hmm = Hmm(2);
}
临时Hmm(2)
的构造有side-effects(它调用user-defined构造函数),所以这个临时没有被使用。然而,临时变量随后被移入局部变量 Hmm hmm
。该局部变量的移动构造函数和析构函数都是微不足道的(因此不会调用用户代码),因此该变量确实未被使用,因为编译器可以证明无论该变量是否存在,程序的行为都是相同的存在(平凡的 ctor + 平凡的 dtor + 没有其他访问变量 = 未使用的变量,如上所述)。如果 Hmm
有一个 non-trivial 移动构造函数或 non-trivial 析构函数,它就不会被使用。
请注意,一个简单的移动构造函数会使 moved-from 对象完好无损,因此它确实没有任何 side-effects(除了初始化正在构造的对象之外)。
这可以通过删除移动构造函数轻松验证,causes both Clang and GCC to complain。
我在 PR 审查中注意到一个未使用的变量,我们想知道为什么编译器没有捕捉到它。因此,我使用 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;
}
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]
那么什么是 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
}
标志:-Wunused-variable 铿锵声:14.0.0 海湾合作委员会:11.3
没有警告的原因是 non-trivial class 类型的变量在您初始化它们时在技术上不是未使用的,但是在您的函数中永远不会访问它们。
考虑这个 example:
struct Trivial {};
struct NonTrivial {
NonTrivial() {
//Whatever
}
};
void test() {
Trivial t;
NonTrivial nt;
}
GCC 警告 Trivial t;
未被使用,因为此声明不会导致任何 user-defined 代码变为 运行;唯一 运行 的是平凡的构造函数和平凡的析构函数,它们是 no-ops。所以根本没有对 Trivial t
执行任何操作,它确实未被使用(它的内存从未被触及)。
NonTrivial nt;
不会引起警告,因为它实际上 被 用于 运行 其构造函数,即 user-defined代码。
这也是为什么编译器不会警告“未使用的锁守卫”或类似的 RAII classes - 他们习惯于 运行 user-defined 构造和销毁代码,这意味着它们被使用(指向对象的指针被传递给user-defined constructor/destructor =地址被使用=使用)。
这可以进一步证明 marking the object's constructor with the gnu::pure
attribute:
struct Trivial {};
struct NonTrivial {
[[gnu::pure]] NonTrivial() {
//Whatever
}
};
void test() {
Trivial t;
NonTrivial nt;
}
在这种情况下,GCC 对它们都发出警告,因为它知道 NonTrivial::NonTrivial()
没有 side-effects,这反过来又使编译器能够证明 [= 的构造和破坏19=] 是一个 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
}
这是equivalent以下内容:
void test() {
Hmm hmm = Hmm(2);
}
临时Hmm(2)
的构造有side-effects(它调用user-defined构造函数),所以这个临时没有被使用。然而,临时变量随后被移入局部变量 Hmm hmm
。该局部变量的移动构造函数和析构函数都是微不足道的(因此不会调用用户代码),因此该变量确实未被使用,因为编译器可以证明无论该变量是否存在,程序的行为都是相同的存在(平凡的 ctor + 平凡的 dtor + 没有其他访问变量 = 未使用的变量,如上所述)。如果 Hmm
有一个 non-trivial 移动构造函数或 non-trivial 析构函数,它就不会被使用。
请注意,一个简单的移动构造函数会使 moved-from 对象完好无损,因此它确实没有任何 side-effects(除了初始化正在构造的对象之外)。
这可以通过删除移动构造函数轻松验证,causes both Clang and GCC to complain。