使用基类、adl koenig 运算符和 sfinae。
namespace ops{
struct subtract_support{
template<class T,
std::enable_if_t<std::is_base_of<subtract_support, T>{}, int> =0
>
friend T operator-( T lhs, T const& rhs ){
lhs-=rhs;
return lhs;
}
};
}
现在从ops::subtract_support 继承会导致- 适用于您的类型。 (注意两行正文:确保 lhs 移出-,与您在 OP 中的版本不同)。
仅命名空间限制会导致您的 - 被发现任何模板生成的类型,其中 tge 参数之一来自您的命名空间,以及其他意外情况:adl 对模板成员来自特定命名空间的类型进行操作。1
此技巧可让您在声明时将每种类型标记为使用此技术。
这种技术对二进制布局的影响几乎为零。但是,如果您需要一些模糊的东西,例如前缀的布局兼容性,则可能需要使用特性类。
现在,这是一个 C++11 解决方案。显然OP需要C++ 03。好吧,最好的方法是像 C++11 一样实现它,这样在升级编译器时可以删除代码。
enable_if 可以很容易地用 C++03 编写。 is_base_of 多写几行:
namespace notstd{
namespace details{
template<class T, class U>
struct base_test{
typedef char no; // sizeof(1)
struct yes { no unused[2]; }; // sizeof(2) or greater
static yes test(T*); // overload if arg is convertible-to-T*
static no test(...); // only picked if first overload fails
// pass a `U*` to `test`. If the result is `yes`, T
// is a base of U. Note that inaccessible bases might fail here(?)
// but we are notstd, good enough.
enum {value= (
sizeof(yes)==sizeof(test((U*)0))
)};
};
}
template<class Base, class T>
struct is_base_of{
enum{value=details::base_test<Base,T>::value};
};
template<bool b, class T=void>
struct enable_if {};
template<class T>
struct enable_if<true, T> {
typedef T type;
};
}
我们还需要调整模板 ADL 运算符中的 SFINAE 以符合 C++03:
namespace ops{
struct subtract_support{
template<class T>
friend
typename notstd::enable_if<notstd::is_base_of<subtract_support, T>::value, T>::type
operator-( T lhs, T const& rhs ){
lhs-=rhs;
return lhs;
}
};
}
Live example.
1 举个例子,如果Foo 是命名空间中带有贪婪- 模板运算符的类型,那么decltype(v0-v1) 其中v0 和v1 是vector<Foo>将是vector<Foo>。这是一个误报(它不会编译)。但是带有自己的- 的vec3<Foo>(3 Foo 的向量空间)会导致同样的歧义。