c – 交换操作符的模板实现,合法吗?

我试图为我的一个类实现一个可交换的加法运算符:

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;
}

Demo

点赞