我试图为我的一个类实现一个交换加法运算符:
struct mytype
{
constexpr mytype(othertype const &);
constexpr mytype operator+(othertype const &rhs) const;
};
template<typename T>
constexpr auto operator+(T const &lhs, mytype const &rhs) -> decltype(rhs + lhs)
{
return rhs + lhs;
}
这样的想法是,只要右侧是mytype
,那么右侧接受的任何内容在左侧也可以接受。
这在icc和Visual Studio上可以正常工作,并且可以无休止地递归来解析gcc和clang上的decltype
(在达到最大模板深度时终止)。
我可以看到,无尽的递归实际上可能更正确,如bug report中所述:在进行重载解析之前,需要进行专门化处理(因为它是重载解析的输入)。
另一方面,商业编译器以某种方式进行管理(无论是偶然还是故意的,都值得商)。
这里的正确行为是什么?
是否可以避免指定operator+
应该可交换的类的完整列表?
可编译的示例:
struct othertype {};
struct mytype
{
constexpr mytype() : value(1) { }
constexpr mytype(int v) : value(v) { }
constexpr mytype(othertype const &o) : value(2) { } // 1
constexpr mytype operator+(mytype const &rhs) const
{
return mytype(value + rhs.value);
}
constexpr mytype operator+(othertype const &rhs) const // 2
{
return mytype(value + 2);
}
int value;
};
template<typename T>
constexpr auto operator+(T const &lhs, mytype const &rhs) -> decltype(rhs + lhs)
{
return rhs + lhs;
}
void test()
{
constexpr mytype mine;
constexpr othertype other;
constexpr auto result = other + mine;
static_assert(result.value == 3);
}
删除转换// 1
时问题消失了,这对我的用例没有帮助。单独的加法运算符// 2
不足以帮助解决decltype
:重载解决方案应该已经解决了,但是问题是在重载解决方案之前发生的。
无限递归发生在专门针对T = othertype
的模板之后:将othertype
转换为mytype
会得到一个在两边带有mytype
的加法表达式,可以再次通过解析模板(即使存在非模板)。
答案 0 :(得分:1)
您可以使用SFINAE限制模板以丢弃operator+<mytype>(mytype const &lhs, mytype const &rhs)
:
template<typename T, typename std::enable_if<!std::is_same<T, mytype>::value, int>::type = 0>
constexpr auto operator+(T const &lhs, mytype const &rhs) -> decltype(rhs + lhs)
{
return rhs + lhs;
}