为什么会收到Wsign转换警告?

时间:2018-12-05 09:27:05

标签: c++ c++11 gcc type-conversion implicit-conversion

我有以下代码:

template <typename T>
struct wrapper {
    T t;
    operator T() { return t; }
    T get() { return t; }
};

int main() {
    int a[10];
    int* x = a;
    wrapper<long unsigned int> y{2};
    std::cout << (x + y); // warning
}

当我用-Wsign-conversion在gcc上编译(在7.3.0和8.2.0上测试)时,我得到“警告:从'long unsigned int'转换为'long int'可能会改变结果的符号”。如果y的类型为long unsigned int,则没有警告。此外,当我显式调用y.get()时,也没有警告:

std::cout << (x + y.get()); // this is ok

为什么会这样?是否存在一些特殊的指针算术规则,这些规则在使用用户定义的转换时无法使用?

1 个答案:

答案 0 :(得分:1)

好像是编译器问题/错误

(感谢@liliscent纠正了我之前在这里所说的内容)

首先,让我们为您提到的所有语句创建一个MCVE

#include <iostream>

template <typename T>
struct wrapper {
    T t;
    operator T() const { return t; }
    T get() const { return t; }
};

int main() {
    int a[10];
    int* x { a } ;
    wrapper<long int> y1{2};
    wrapper<unsigned int> y2{2};
    wrapper<long unsigned int> y3{2};

    std::cout << (x + y1) << '\n';
    std::cout << (x + y2) << '\n';
    std::cout << (x + y3) << '\n'; // this triggers a warning
    std::cout << (x + y3.get()) << '\n';
}

并使用GCC 8.2.0,我们get

<source>: In function 'int main()':
<source>:20:23: warning: conversion to 'long int' from 'long unsigned int' may change the sign of the result [-Wsign-conversion]
     std::cout << (x + y3) << '\n';
                       ^~
Compiler returned: 0

在链接上,您将看到如何:

  • GCC在所有(最新)版本中都会发出此错误。
  • Clang在版本6.0中发出此错误。
  • Clang不会 not 在7.0版中发出此错误。

所以一定是一些特殊情况符合标准。

...但是不要进入“这里是龙”的领域。

现在,我敢肯定有一个复杂的技术解释,说明为什么您仅在这些流式处理语句的第3条上出现错误。但我认为从实际使用的角度来看,这并不重要。如果您坚持只向指针添加适当的整数(如使用.get()语句一样),则不会收到该警告。

您看到的,您正在尝试将用户定义的类型添加到指针中-通常这没有多大意义。的确可以将结构转换为整数,但是依靠这种隐式转换 可以使您进行选择转换其他操作数或未考虑的其他转换路径的操作。此外,在某些情况下,您可能会“敲定”标准中有关隐式转换何时合法的某些深奥条款,编译器可能会或不会完全正确地实现隐式转换(请参阅@cppcleaner的注释)。

因此,只需在代码中使用x + y3.get(),就不必担心这些神秘的极端情况。

我在this answer中做了类似的论点,涉及空字符串的索引0的使用(是的,就是这样)。