鉴于此代码:
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提升为浮点数,但显然它并不想这样做。有办法解决这个问题吗?
答案 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>::type
是int
:你可能有一些专业化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 +的实现,编译器就不知道你的期望,并且这个分析非常复杂。