Подтвердить что ты не робот

Рекомендации по выполнению перегрузки оператора constexpr?

Рассмотрим простой класс int Wrapper с перегруженным умножением operator*= и operator*. Для "перегрузки" старого стиля можно определить operator* в терминах operator*=, и есть даже такие библиотеки, как Boost. Операторы и его современное воплощение df.operators by @DanielFrey, которые уменьшают шаблон для вас.

Однако для вычислений времени компиляции с использованием нового С++ 11 constexpr это удобство исчезает. A constexpr operator* не может вызывать operator*=, потому что последний изменяет его (неявный) левый аргумент. Кроме того, нет перегрузки на constexpr, поэтому добавление дополнительного constexpr operator* к существующему operator* приводит к двусмысленности разрешения перегрузки.

Мой текущий подход:

#include <iostream>

struct Wrap
{
    int value;    

    Wrap& operator*=(Wrap const& rhs) 
    { value *= rhs.value; return *this; }

    // need to comment this function because of overloading ambiguity with the constexpr version
    // friend Wrap operator*(Wrap const& lhs, Wrap const& rhs)
    // { return Wrap { lhs } *= rhs; }    

    friend constexpr Wrap operator*(Wrap const& lhs, Wrap const& rhs)
    { return { lhs.value * rhs.value }; }
};

constexpr Wrap factorial(int n)
{
    return n? factorial(n - 1) * Wrap { n } : Wrap { 1 };    
}

// want to be able to statically initialize these arrays
struct Hold
{
    static constexpr Wrap Int[] = { factorial(0), factorial(1), factorial(2), factorial(3) };
};

int main() 
{
    std::cout << Hold::Int[3].value << "\n"; // 6
    auto w = Wrap { 2 };
    w *= Wrap { 3 };
    std::cout << w.value << "\n"; // 6
}

Живой вывод здесь. Мои проблемы с этим:

  • дублирование логики умножения как в operator*=, так и operator* вместо operator* выражается через operator*=
  • следовательно, Boost.Operators больше не работает, чтобы уменьшить шаблон для записи многих других арифметических операторов.

Вопрос: это рекомендуемый способ С++ 11 иметь как время выполнения operator*=, так и смешанное время выполнения/время компиляции constexpr operator*? Изменяет ли С++ 14 что-либо здесь, например? уменьшить логическое дублирование?

ОБНОВЛЕНИЕ. Ответ от @AndyProwl принимается как идиоматический, но, согласно предложению @DyP, на С++ 11 можно уменьшить дублирование логики за счет дополнительного назначения и контр- интуитивно понятный стиль

    // define operator*= in terms of operator*
    Wrap& operator*=(Wrap const& rhs) 
    { *this = *this * rhs; return *this; }
4b9b3361

Ответ 1

Я не мог найти идиоматическое решение для С++ 11 (хотя, как обходной путь, DyP предложение кажется мне приемлемым).

Однако в С++ 14, где constexpr не подразумевает const (см. Приложение C.3.1 документа C + +14 Standard Draft n3690), вы можете просто определить как operator *=, так и operator * как constexpr, и определить последнее в терминах первого, как обычно:

struct Wrap
{
    int value;    

    constexpr Wrap& operator *= (Wrap const& rhs) 
    { value *= rhs.value; return *this; }

    friend constexpr Wrap operator * (Wrap const& lhs, Wrap const& rhs)
    { return Wrap(lhs) *= rhs; }    
};

Вот живой пример, где приведенная выше программа скомпилирована с помощью -std=c++1y на Clang - К сожалению, GCC пока что не реализует это правило.