g++ 4.9.0
-O2 -std = c++ 11

template<class T>
struct vec3 {
    T x, y, z;
    vec3() = default;
    vec3(const vec3<T> &other) = default;
    vec3(T xx, T yy, T zz) { x = xx; y = yy; z = zz; }
    vec3<T> operator-(const vec3<T> &other) {
      return vec3<T>{ x - other.x, y - other.y, z - other.z };
    }
};

int main() {
    vec3<char> pos{ 0, 0, 0 };
    vec3<char> newPos{ 0, 0, 0 };
    auto p = pos - newPos;

    return 0;
}

我得到警告:
!!warning: narrowing conversion of ‘(((int)((vec3<char>*)this)->vec3<char>::x) - ((int)other.vec3<char>::x))’ from ‘int’ to ‘char’ inside { } [-Wnarrowing]

但是,如果我在(...)函数内部使用由{...}插入的operator-来执行此操作,警告将消失。为什么?

最佳答案

首先,为什么要缩小?那来自§5/10:

在4.5/1中定义了积分促销的地方:

那么在我们的例子中,我们的decltype(char + char)int,因为char的转换等级小于int,因此在调用int之前,两者都被提升为operator+。现在,我们将int传递给采用char的构造函数。根据定义(第8.5.4/7节,特别是7.4):

根据8.5.4/3节的规定,在列表初始化中明确禁止使用它(强调我的,“参见下文”实际上是指我刚才在上面复制的内容):

这就是为什么vec3<T>{int, int, int}会警告您的原因:该程序格式不正确,原因是整数提升要求对所有表达式进行缩小转换。现在,有关“格式错误”的声明仅在列表初始化的上下文中出现。这就是为什么如果不使用{}s初始化 vector ,则看不到该警告的原因:

vec3<T> operator-(const vec3<T> &other) {
    // totally OK: implicit conversion from int --> char is allowed here
    return vec3<T>( x - other.x, y - other.y, z - other.z );
}
对于解决此问题-仅调用构造函数而不进行列表初始化可能是最简单的解决方案。另外,您可以继续使用列表初始化,而只需对构造函数进行模板化:
template <typename A, typename B, typename C>
vec3(A xx, B yy, C zz)
: x(xx) // note these all have to be ()s and not {}s for the same reason
, y(yy)
, z(yy)
{ }

09-20 22:14