我试图为我的一个类实现一个可交换的加法运算符:
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所述:在进行重载解析之前需要专门化(因为它是重载决策的输入).
另一方面,商业编制者以某种方式管理(无论是偶然还是故意,都可能存在争议).
这里的正确行为是什么?
是否可以避免指定运算符应该是可交换的类的完整列表?
可编辑的例子:
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的加法表达式,这也可以通过模板解析(即使存在非模板).
最佳答案 您可以使用SFINAE限制模板以丢弃运算符< 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;
}