我有以下代码:
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
为什么会这样?是否有一些特殊的指针算法规则在使用用户定义的转换时无法使用?
最佳答案 看起来像编译器问题/错误
(感谢@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中发出此错误.
>在版本7.0中,Clang不会发出此错误.
所以它必须是一些角落案例w.r.t.标准合规.
……但不要进入“Here Be Dragons”领域.
现在,我确信有一个复杂的技术解释,说明为什么你只能在这些流语句的第3个上得到错误.但我声称从实际使用角度来看并不重要.如果坚持只向指针添加正确的整数 – 就像使用.get()语句一样 – 你就不会得到那个警告.
您看,您正在尝试将用户定义的类型添加到指针 – 这通常没有多大意义.确实,您的结构可以转换为整数,但是依赖于此隐式转换会打开诸如转换其他操作数的选择或您未考虑的其他转换路径之类的事情.更重要的是,在这些情况下,您可能会“触及”标准中关于隐式转换何时合法的一些深奥条款,编译器可能会或可能不会以完美的正确性实现(请参阅@ cppcleaner的评论).
所以只需在代码中使用x y3.get(),您就不必担心这些深奥的角落情况.
我在this answer中提出了一个类似的论点,关于使用空字符串的索引0(是的,这是一件事).