Компилятор выдает "неоднозначную перегрузку для оператора" - PullRequest
2 голосов
/ 01 мая 2020

Я изучаю, как использовать std :: chrono, и хочу сделать шаблонный класс Timer простым в использовании (определено в timer.h). Программа тестирования прошла успешно, и все работало нормально, пока я не попытался использовать свой новый таймер в программе с определением некоторых операторов шаблонов, которые противоречат операторам, используемым внутри таймера.

Внутри таймера, который я должен использовать operator- между двумя переменными (start_time и end_time) типа std::chrono::time_point, чтобы получить переменную duration, содержащую истекшее время.

В другом заголовке (algebra.h) I реализована перегрузка двоичного файла operator-, чтобы сделать разницу между двумя std::vector или двумя std::array, а также определяемым пользователем контейнером, снабженным operator[] и size() функцией-членом.

template<typename pointType>
pointType operator-(pointType a, const pointType & b){
    for(int i = 0; i < a.size(); ++i){
        a[i] = a[i] - b[i];
    }
    return a;
}

Когда я пытаюсь включить как timer.h, так и algebra.h, компилятор выдает ошибку, говорящую "неоднозначная перегрузка для оператора-", предлагая в качестве возможных кандидатов как оператор в algebra.h, так и оператор, реализованный в <chrono> .

Я не понимаю, почему это неоднозначно, поскольку pointType не может быть выведено как std::chrono::time_point, потому что оно не имеет operator[] и size() функции-члена. * 103 0 *

PS Я попробовал что-то еще, чтобы решить это, но я только запутался, тестируя программу, которая использует std::valarray. Когда я включаю <valarray> и "algebra.h" и пытаюсь провести различие между двумя значениями, я ожидал, что компилятор пожалуется на неоднозначное определение operator-, поскольку std::valarray уже имеет реализацию для бинарных операторов. Но этого не происходит: он компилируется с использованием реализации <valarray>. Почему это не выдает ошибку?

1 Ответ

1 голос
/ 01 мая 2020

Это неоднозначно, потому что компилятор просматривает сигнатуру функции только для проверки на неоднозначность, а не тело функции. В вашем примере это сигнатура функции:

template<typename pointType>
pointType operator-(pointType a, const pointType & b)

Здесь параметр шаблона pointType может быть выведен как std::chrono::time_point. Однако в заголовке chrono для std::chrono::time_point (https://en.cppreference.com/w/cpp/chrono/time_point/operator_arith2) уже объявлен двоичный оператор минус. Именно это и вызывает ошибку неоднозначности.

Чтобы решить эту проблему, вы должны сначала подумать, нужен ли вам такой обобщенный c двоичный оператор минус. Проблема, с которой вы сталкиваетесь в настоящее время, не будет уникальной для std::chrono::time_point, но также будет возникать с любым другим заголовком, который содержит класс с двоичным оператором минус-члена или не являющимся членом, где оба аргумента имеют один и тот же тип (или могут быть неявно). преобразовать в тот же тип). Возможно простой набор перегрузок функций для рассматриваемых типов:

template<typename T>
std::vector<T> operator-(const std::vector<T>& a, const std::vector<T>& b);

template<typename T, size_t N>
std::array<T,N> operator-(const std::array<T,N>& a, const std::array<T,N>& b);

Это был бы самый безопасный вариант. Вы также не можете использовать перегрузку операторов вообще и придерживаться обычной функции:

template<typename T>
T pointwise_subtract(const T& a, const T& b);

Если у вас есть компилятор c ++ 20, вы можете использовать понятия. Если вы настаиваете на использовании шаблонов операторов, не являющихся членами, вам, возможно, придется использовать метапрограммирование шаблонов на основе SFINAE, более продвинутый и менее читабельный метод:

//enable this template if the type T has a member method "size" and 
//    subscript operator accepting variables of type "size_t"
template<typename T, typename=std::void_t<
                            decltype(std::declval<T>().size()),
                            decltype(std::declval<T>()[std::declval<size_t>()])
                                         >
T operator-(const T& a, const T& b);

Это устранит вашу ошибку неоднозначности.

...