Ruf*_*ind 6 c++ operator-overloading sfinae template-meta-programming
在尝试wrapper为另一种类型编写类型时T,我遇到了一个相当令人讨厌的问题:我想定义一些二进制运算符(例如+)将任何操作转发wrapper到基础类型,但我需要这些运算符接受任何潜在的涉及的组合wrapper:
wrapper() + wrapper()
wrapper() + T()
T() + wrapper()
Run Code Online (Sandbox Code Playgroud)
天真的方法涉及直接编写所有潜在的重载.
但是我不喜欢编写重复的代码并且想要更多的挑战,所以我选择使用非常通用的模板来实现它并用一个限制潜在的类型enable_if.
我的尝试显示在问题的底部(对不起,这是我能想到的最小).问题是它会遇到无限递归错误:
test() + test(),编译会查看所有潜在的重载.enable_if子句,它应该阻止它成为有效的重载,但编译器只是忽略它并尝试计算第decltype一个,这需要...operator+(test, test).我们回到了我们开始的地方.GCC非常适合吐出错误; Clang只是段错误.
什么是一个好的,干净的解决方案?(请记住,还有其他运营商需要遵循相同的模式.)
template<class T>
struct wrapper { T t; };
// Checks if the type is instantiated from the wrapper
template<class> struct is_wrapper : false_type {};
template<class T> struct is_wrapper<wrapper<T> > : true_type {};
// Returns the underlying object
template<class T> const T& base(const T& t) { return t; }
template<class T> const T& base(const wrapper<T>& w) { return w.t; }
// Operator
template<class W, class X>
typename enable_if<
is_wrapper<W>::value || is_wrapper<X>::value,
decltype(base(declval<W>()) + base(declval<X>()))
>::type operator+(const W& i, const X& j);
// Test case
struct test {};
int main() {
test() + test();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
这是相当笨重的解决方案,我宁愿不使用,除非我必须:
// Force the evaluation to occur as a 2-step process
template<class W, class X, class = void>
struct plus_ret;
template<class W, class X>
struct plus_ret<W, X, typename enable_if<
is_wrapper<W>::value || is_wrapper<X>::value>::type> {
typedef decltype(base(declval<W>()) + base(declval<X>())) type;
};
// Operator
template<class W, class X>
typename plus_ret<W, X>::type operator+(const W& i, const X& j);
Run Code Online (Sandbox Code Playgroud)
作为 TemplateRex 注释的补充,我建议使用宏来实现所有重载并将运算符作为参数:
template<class T>
struct wrapper { T t; };
#define BINARY_OPERATOR(op) \
template<class T> \
T operator op (wrapper<T> const& lhs, wrapper<T> const& rhs); \
template<class T> \
T operator op (wrapper<T> const& lhs, T const& rhs); \
template<class T> \
T operator op (T const& lhs, wrapper<T> const& rhs);
BINARY_OPERATOR(+)
BINARY_OPERATOR(-)
#undef BINARY_OPERATOR
// Test case
struct test {};
test operator+(test const&, test const&);
test operator-(test const&, test const&);
int main() {
test() + test();
wrapper<test>() + test();
test() - wrapper<test>();
return 0;
}
Run Code Online (Sandbox Code Playgroud)