【问题标题】:Template implementation for commutative operators, legal?交换运算符的模板实现,合法吗?
【发布时间】: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


【解决方案1】:

您可以使用 SFINAE 限制您的模板以丢弃 operator+&lt;mytype&gt;(mytype const &amp;lhs, mytype const &amp;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;
}

Demo

【讨论】:

    猜你喜欢
    • 2021-04-08
    • 1970-01-01
    • 2014-12-07
    • 2017-05-03
    • 2011-05-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-26
    相关资源
    最近更新 更多