Реализация шаблона для коммутативных операторов, законно? - PullRequest
4 голосов
/ 07 марта 2019

Я пытался реализовать оператор коммутативного сложения для одного из моих классов:

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 и идет в бесконечную рекурсию, разрешая decltype для gcc и clang (прекращается, когда достигается максимальная глубина шаблона).

Я вижу, что бесконечная рекурсия на самом деле может быть более правильной, как объяснено в отчете об ошибке : специализация необходима перед разрешением перегрузки (поскольку она является входом дляразрешение перегрузки).

1013 * с другой стороны, коммерческие компиляторы как-то управлять (случайно ли или с целью, вероятно, спорно). 1015 * Что такое правильное поведение здесь? 1017 *Можно ли избежать указания полного списка классов, для которых operator+ должен быть коммутативным?

Компилируемый экзаменple:

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 Ответ

1 голос
/ 07 марта 2019

Вы можете ограничить свой шаблон с помощью 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;
}

Демо

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...