使用模板的C ++隐式types转换

我有一个模板class A

 template <unsigned int m> class A { public: A(int) {} }; 

其中有一个来自int的构造函数。 我有一个操作:

 template<unsigned int m> A<m> operator+(const A<m>&, const A<m>&) { retrun A<m>(0); } 

但是当我打电话给:

 A<3> a(4); A<3> b = a + 5; A<3> c = 5 + a; 

我想int被隐式转换为A,但是编译器会抛出错误。

有什么优雅的方式来启用隐式转换,而不使用这样的解决scheme:

  • a + A<m>(5)
  • operator+<3>(a, 5)

解决scheme已经在这个答案中显示 。 现在,更多关于这个问题…

代码中的问题是如何执行重载parsing。 当一个模板函数被考虑用于重载parsing时,编译器将对参数进行types推导,并提出一个与调用相匹配的typesreplace,否则将无法应用该模板,将其从潜在候选集合中移除并继续。 此时的问题是,types推导只能推导出精确的匹配(可能具有额外的const / volatile限定)。 由于匹配是准确的,编译器将不会使用任何转换(再次,除cv外)。

这个最简单的例子发生在std::maxstd::min函数中:

 unsigned int i = 0; std::min( i, 10 ); // Error! 

types推导将在template <typename T> min( T const &, T const & )推导出T对于第一个参数是unsigned ,而对于第二个参数则是int ,它们不同,编译器将放弃这个模板函数。

答案中提出的解决scheme是使用该语言的一个function,使您能够在类定义中定义一个非成员的朋友函数。 模板的优点在于,对于模板的每个(不同的)实例,编译器都会在名称空间级别创build一个免费的非模板函数,该函数具有通过replace好友声明中实例的实际types而获得的签名:

 template <typename T> class test { friend test operator+( test const & lhs, test const & rhs ) { // [1] return test(); } } test<int> t; // [2] 

在上面的例子中,编译器允许你在[1]的类范围内添加friend函数的定义。 然后当你在[2]中实例化模板时,编译器将生成一个自由函数:

 test<int> operator+( test<int> const & lhs, test<int> const & rhs ) { return test<int>(); } 

无论是否使用该函数(这不同于模板类成员函数,都是按需实例化的)。

这里的魔力有多方面的。 第一部分是它通常是为每个和所有的实例化types定义非模板函数,这样你就获得了通用性,同时当参数不完美匹配时,重载parsing能够使用这个函数的好处。

因为它是一个非模板函数,所以编译器可以调用这两个参数的隐式转换,并且会得到预期的行为。

此外,不同types的魔术继续查找,因为如此定义的函数只能通过依赖于参数的查找find, 除非它也在名称空间级别声明,在我们的情况下,这不能以通用方式进行。 这可能是好的或坏的,这取决于你想如何考虑它…

因为它只能通过ADLfind,除非至less有一个参数已经是所需types(即不会被用于对两个参数执行转换),否则不会被考虑。 缺点是,除非实际调用它,否则不可能引用该函数,这意味着您无法获得函数指针。

(更多关于这里的模板友情,但是请注意,在这种特殊情况下,所有其他变体将无法执行隐式转换)。

每个尝试使用模板的运营商都需要至less一秒的重载。 但是可以通过在类中定义运算符来避免这种情况:

 template <unsigned int m> class A { public: A(int) {} inline friend A operator+(const A& a, const A& b) { return A(0); } }; 

适用于a+55+a

添加这个运算符

 template<unsigned int m> A<m> operator+(const A<m>&, const int&) { return A<m>(0); } 

或者试试这个

 template <unsigned int m> class A { friend const A operator+(const A& a, const A& b) { return A(0); } public: A(int) {} // OR FOR UNARY // const A operator+(const A &a) const {return A(0);} }; int main(){ A<3> a(4); A<3> b = a + 5; A<3> c = 5 + a; 

}

您可以尝试为您的A类的模板添加一个额外的“政策”types参数,以确定实际所需的转换types。 例如:

 template <unsigned int m, typename ConvVal = int> class A { public: typedef ConvVal conv_val; A(ConvVal) {} }; template<template <unsigned int, class U> class T, unsigned int m, typename U> T<m, U> operator+(const T<m, U>&, const T<m, U>&) { return T<m, U>(0); } template<template <unsigned int, class U> class T, unsigned int m, typename U> T<m, U> operator+(const T<m, U>&, const typename T<m, U>::conv_val&) { return T<m, U>(0); } template<template <unsigned int, class U> class T, unsigned int m, typename U> T<m, U> operator+(const typename T<m, U>::conv_val&, const T<m, U>&) { return T<m, U>(0); } int main() { A<3> a(4); A<3> b = a + 5; return 0; } 

现在你的A类将接受一个默认为inttypes的模板参数,它定义了你将允许自动转换的实际types。 只需要对operator+函数进行三次重载,一次对于不带转换值的版本,将采用typesA<m, T>显式类,另一个用于将采用转换types的两个operator+版本。 在上面的代码中,我将它泛化为更一般的types,这样就可以使用具有适当模板签名的其他任何类,并定义了conv_val typedef。