【发布时间】:2019-07-28 23:14:18
【问题描述】:
我尝试为我的一个类实现一个可交换加法运算符:
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 的加法表达式,这同样可以通过模板解决(即使一个非模板存在)。
【问题讨论】:
-
这个运算符怎么用?您能否向我们展示一个 minimal reproducible example 来展示您遇到的问题?即使我们无法复制问题,看看你如何使用它也会很有用。了解您对成员重载的实现以及
othertype是什么可能非常有用。 -
对了,你真的需要非显式转换操作符吗?如果您将其删除或设为
explicit,会发生什么情况? -
@Someprogrammerdude,这是重构的一部分 — 从长远来看,
mytype应该替换othertype,因此它需要可转换以允许逐步替换实例而不破坏其余部分程序。
标签: c++ c++11 language-lawyer