回答自己...
问题似乎源于 ISO C++ 标准 7.3.3.5:
using-declaration 不应命名模板 ID。
这会阻止接受:using Foo::operator+<42>
.
作为一种解决方法,我发现以下解决方案可以满足我的需要,但代价是额外的命名空间重定向。代码可能仍需要一些按摩,但它确实可以在用户端以最少的重复完成任务。
在此处查看工作版本。
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
。这允许使用所需的参数
来实例化类型。
运算符只需通过外部定义和参数化的类型调用静态方法。
ADL 负责其余的工作。Foo
在上面的示例中,用户创建了 2 个新的命名空间,Boo
并Goo
具有 2 个不同的加号运算符参数化。最后,在使用时,用户会带入所需版本operator+
的using directive
.
在这种方法中,似乎没有指定默认参数值的选项。
在实际代码中,运算符本身将存储在一个片段文件中,以便在声明参数化类型(在示例中)#include
之后编辑到代码中。Baz
拿 2
这是一个更简洁的版本,它使用了一个简单的模板化特征类,并避免了额外的命名空间和操作符重定向功能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;
}