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

时间:2015-10-24 10:32:43

标签: c++ templates types type-conversion

鉴于此代码:

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提升为浮点数,但显然它并不想这样做。有办法解决这个问题吗?

2 个答案:

答案 0 :(得分:6)

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

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

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

Type<U>

这样做的原因是因为编译器不能也不能确定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>个添加,那么您就不能使用这种完全相同的方法:您不能使用成员函数。但是如果你已经将这一个运算符作为成员函数,并且如果add是可交换的,则可以使用非成员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>再次,直到编译器达到其内部限制,甚至可能崩溃。)

答案 1 :(得分:0)

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

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

是否应该将 val int提升为float或将Type键入其他内容。 编译器不知道Type

解决这个问题:

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;
}

为了举例说明为什么编译器决定将什么推广到什么样的东西太复杂,你的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;
}

如果不分析Type的定义和operator +的实现,编译器就不知道你的期望,并且这个分析非常复杂。