模板化重载运算符的显式实例化

时间:2016-04-27 15:25:25

标签: c++ operator-overloading template-instantiation

以下代码works

struct A
{
   int v = 3;
};

namespace Foo
{
   template <int k=11>
   int operator+(A const& lhs, A const& rhs)
   {
      return lhs.v + rhs.v + k;
   }
}

using Foo::operator+;

int main()
{
   A a1, a2;
   std::cout << a1 + a2 << std::endl;
   return 0;
}

using Foo::operator+;指令将Foo::operator+带入外部查找范围,operator+调用中使用cout时,默认模板值为11,并且结果如预期的那样:17(= 3 + 3 + 11)。

我的问题是如何更改using子句以使用 -default模板值显式实例化operator+函数?

using Foo::operator+<42>行不起作用 这是由于ISO C ++标准7.3.3.5:使用声明不应命名模板ID。
有没有解决的办法?

2 个答案:

答案 0 :(得分:2)

回答自己...... 问题似乎源于ISO C ++标准7.3.3.5:

  

使用声明不得命名模板ID。

这可以阻止接受:using Foo::operator+<42>

作为一种解决方法,我找到了以下解决方案,它以我需要的方式完成了额外的命名空间重定向。代码可能仍然需要一些按摩,但它确实完成了任务,用户方面的重复最少。

查看有效版本here

struct A
{
   int v = 0;
};

template <int k>
struct Bar
{
   static int plus(A const& lhs, A const& rhs)
   {
      return rhs.v + lhs.v + k;
   }
};

namespace Boo
{
   using Baz = Bar<42>; // same as `typedef Bar<42> Baz;`

    //#include "foo_operators.h"
    namespace Foo
    {
       int operator+(A const& rhs, A const& lhs)
       {
          return Baz::plus(lhs, rhs);
       }
    }
}

namespace Goo
{
   using Baz = Bar<3>; 

    //#include "foo_operators.h"
    namespace Foo
    {
       int operator+(A const& rhs, A const& lhs)
       {
          return Baz::plus(lhs, rhs);
       }
    }
}


using namespace std;
int main()
{
   {
      using Boo::Foo::operator+;
      A a1, a2;
      cout << a1 + a2 << endl;
   }

   {
      using Goo::Foo::operator+;
      A a1, a2;
      cout << a1 + a2 << endl;
   }

   return EXIT_SUCCESS;
}

// In real code extract to foo_operators.h: the partial file snippets to get #included multiple times
// namespace Foo
// {
//    int operator+(A const& rhs, A const& lhs)
//    {
//       return Baz::plus(lhs, rhs);
//    }
// }

我们的想法是使用静态方法Foo的结构模板替换Bar命名空间 这允许使用所需的参数实例化Foo类型 运算符只需通​​过外部定义和参数化类型调用静态方法 ADL负责其余的工作。

在上面的示例中,用户创建了2个新的命名空间BooGoo,以获得plus运算符的2个不同参数化。最后,在使用时,用户会使用operator+带来所需的using directive版本。

在这种方法中,似乎没有指定默认参数值的选项。

在实际代码中,操作符本身将存储在一个片段文件中,在声明参数化类型(示例中为#include)后Baz编入代码。

选择2

Here's一个更简洁的版本,它使用一个简单的模板化traits类,并避免额外的命名空间和操作符重定向函数plus

template <int k>
struct op_traits_t
{
   static const int K = k;
};


namespace Boo
{
   using op_traits = op_traits_t<42>; // same as `typedef op_traits_t<42> op_traits;`
   //#include "foo_operators.h"
    // this is a partial file snippet
    int operator+(A const& rhs, A const& lhs)
    {
       return rhs.v + lhs.v + op_traits::K; 
    }
}

namespace Goo
{
   using op_traits = op_traits_t<3>;
   //#include "foo_operators.h"
    // this is a partial file snippet
    int operator+(A const& rhs, A const& lhs)
    {
       return rhs.v + lhs.v + op_traits::K; 
    }
}

int main()
{
   {
      using Boo::operator+;
      A a1, a2;
      cout << a1 + a2 << endl;
   }

   {
      using namespace Goo;
      A a1, a2;
      cout << a1 + a2 << endl;
   }

   return EXIT_SUCCESS;
}

答案 1 :(得分:1)

std::cout << operator+<12>(a1, a2) << std::endl;

但不要这样做。操作员+应该表现得毫不奇怪。

使用命名函数:

namespace Foo
{
   template <int k=11>
   int add_plus_k(A const& lhs, A const& rhs)
   {
      return lhs.v + rhs.v + k;
   }
}