为什么会收到Wsign-conversion警告?

5

I have following code:

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
}

当我使用gcc编译它(在7.3.0和8.2.0上进行了测试),并加上-Wsign-conversion参数时,我会收到警告:“warning: conversion to 'long int' from 'long unsigned int' may change the sign of the result”。如果y的类型是long unsigned int,则不会有警告。而且,当我明确调用y.get()时,也没有警告:

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

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

1
请注意,我记得指针算术运算仅适用于数组元素。 - Daniel Langr
2
对于 x + y,会执行重载决议并选择内置候选项 T* operator+(T*, std::ptrdiff_t);。然而,long unsigned intstd::ptrdiff_t 的转换实际上并没有发生,而 GCC 在生成警告时似乎忽略了这一点。 - cpplearner
@DanielLangr没错,我对问题进行了编辑,现在a是数组类型。 - Igor
1个回答

2

似乎是编译器问题/bug

(感谢@liliscent纠正我之前的错误说法)

首先,让我们为你提到的所有语句创建一个单一的最小可复现示例

#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时,我们得到以下结果:

<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在7.0版本中不会发出此错误。
因此,这必须是与标准合规性有关的一些边角情况。
但不要进入“龙之地”。
现在,我确信有一个复杂的技术解释,说明为什么只在这三个流式语句中得到错误。但是我认为从实际使用的角度来看,这并不重要。如果您只添加适当的整数到指针中 - 就像您在.get()语句中所做的那样 - 您就不会收到警告。
您看,您正在尝试将用户定义的类型添加到指针中 - 这通常没有太多意义。确实,您的结构可以转换为整数,但是依赖于隐式进行此转换打开了其他操作数的选择或其他未考虑的转换路径。而且,这些是您可能会在标准中遇到某些关于何时隐式转换是合法的鲜为人知条款的情况,在这种情况下,编译器可能无法完全正确地实现它们(请参见@cppcleaner的评论)。
因此,在您的代码中只需使用x + y3.get(),您就不必担心这些鲜为人知的边角情况。
我在此答案中提出了类似的论点,关于使用空字符串的索引0(是的,那是一件事)。

2
我担心这并没有回答问题。如果这是答案的第二部分,而第一部分解释了原因,那么我完全可以理解。这样答案就会变成“这就是为什么;但请做些明智的事情,比如这样。”但是没有第一部分,它就不是一个答案。理解为什么会发生某事可能比“一个有效的解决方案”更有价值。 - Angew is no longer proud of SO
@Angew:在某些情况下,理解为什么会发生某事是有价值的。但在这种情况下,我认为不理解为什么会发生某事是有价值的(除非你正在探索GCC的行为而不是编写C++)。但我会尝试稍微编辑一下以强调这一点。 - einpoklum
@Angew... 但是我还是给答案加了一个前半部分。 - einpoklum
似乎clang没有警告你,这是错误的。你应该传递“-Wsign-conversion”来启用警告,或者使用“-Weverything”。 - llllllllll
@einpoklum 这个警告在clang 6.0中存在,但在7.0中不存在。可能是一个bug。 - llllllllll

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接