为什么模板类型参数不能隐式强制转换?

Why can't template type arguments be implicitly casted?

本文关键字:转换 类型参数 不能 为什么      更新时间:2023-10-16

给定此代码:

template<typename T>
struct Type
{
    T value;
};
template<typename T>
Type<T> operator+(const Type<T>& type, T val)
{
    Type<T> type;
    type.value += val;
    return type;
}
int main()
{
    Type<float> type;
    type = type + 2;
}

我在 MSVC 中收到错误:

Error C2782 'Type<T> operator +(const Type<T> &,T)': template parameter 'T' is ambiguous
Error C2676 binary '+': 'Type<float>' does not define this operator or a conversion to a type acceptable to the predefined operator 
Error no operator "+" matches these operands

我认为它只会将 int 提升为浮点数,但显然它不想这样做。有没有办法解决这个问题?

问题是编译器无法确定在Type<float> + int中,你想调用operator+<float>(强制从intfloat的转换(而不是operator+<int>(强制从Type<float>Type<int>的转换(。当然,第二种解释会导致错误,但此时已经检测到并报告了歧义。

在这种特定情况下,您可能希望Type<T> + U始终返回Type<T> 。在这种情况下,您需要确保编译器无法确定要转换为的Type<U>,无论是否支持转换。

@dyp评论部分发布,如何使用帮助程序模板实现此目的。除了不同的名称外,这基本上是:

template <typename T>
struct identity { typedef T type; };
template <typename T>
Type<T> operator+(const Type<T>& type, typename identity<T>::type val) { ... }

这样做的原因是因为编译器不能也不允许弄清楚使用哪个T来制作identity<T>::typeint:你可以在某个地方对identity进行一些专业化,也可以identity<haha>::type int。因此,只有type用于确定T

它有效,但在这种特殊情况下,我认为更简单的方法是将operator+作为成员函数。成员函数中的隐藏this参数尚未转换为其他类型的参数。

template<typename T>
struct Type
{
    T value;
    Type operator+(T val) { ... }
};

注意:如果您还想支持T + Type<T>添加,则不能使用完全相同的方法:不能为此使用成员函数。但是,如果您已经将这一个运算符作为成员函数,并且如果加法是可交换的,则可以使用非成员operator+进行仅交换操作数的相反转换。@dyp已经指出了一种使用两个模板参数的替代方法,如果与此处的交换结合使用,该方法效果很好:

template <typename T, typename U>
auto operator+(T val, const Type<U>& type) -> decltype(type.operator+(val)) {
  return type.operator+(val);
}

现在,如果你写int + Type<float>operator+<int, float>被实例化和调用,然后最终调用Type<float>.operator+(float)。所以val仍然被转换为float.

(编写type.operator+(val)而不是type + val的原因是,否则无效添加Type<T> + Type<U>将被交换到Type<U> + Type<T>,后者会再次交换回Type<T> + Type<U>,直到编译器达到其内部限制甚至可能崩溃。

编译器没有关于如何在用户类型和标准类型之间使用运算符的直接要求: 类型是用户类型。

Type<float> + int has no standard rules defined on the priority of casts.

它应该将 val int 提升为浮点数还是将类型类型提升为其他内容。编译器不知道类型

要解决此问题:

template<typename T, typename U>
Type<T> operator+(const Type<T>& type, U val)
{
    // Type<T> type; also do not ovewrite the parameter
    type.value += val; // here it knows that type.value is float
                       // standard rules for float + int
    return type;
}

举个例子,为什么编译器决定将什么提升到什么太复杂了,你的类型可能是:

template<typename T>
struct Type
{
    T key;
    int value;
};
template<typename T>
Type<T> operator+(const Type<T>& type, T val)
{
    type.value += val;
    return type;
}

如果不分析类型的定义和运算符+的实现,编译器就不知道你的期望是什么,并且做这个分析是非常复杂的。