交换运算符的模板实现,合法吗?

时间:2019-03-07 06:37:06

标签: c++ c++11 language-lawyer

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

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的加法表达式,可以再次通过解析模板(即使存在非模板)。

1 个答案:

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

Demo