Константный параметр в шаблоне по умолчанию

203
15 ноября 2021, 09:20

Подскажите в чем ошибка при реализации следующей ситуации:

Есть 2 класса дробей, сущность которых идентична за исключением некоторых особенностей, зависящих от флага "сократимые дроби"/"не сократимые дроби". По умолчанию дроби сократимые, поэтому класс задаю так:

template <bool isReduced = false>
class CFraction
{
public:
    int     m_numenator;
    int     m_denumenator;
public:
    // конструкторы
    template <bool isReduced = false>
    CFraction()
        : m_numenator(0), m_denumenator(1)
    {}
    template <bool isReduced = false>
    CFraction(const int numenator)
        : m_numenator(numenator), m_denumenator(1)
    {}
    template <bool isReduced = false>
    CFraction(const int numenator, const int denumenator)
        : m_numenator(numenator), m_denumenator(denumenator)
    {}
};

во первых при попытке

const CFraction num(1, 3);

сразу выдаётся ошибка

Error C2955 'CFraction': use of class template requires template argument list fractions

Error C2514 'CFraction': class has no constructors fractions

Почему так?

И второй вопрос - для сравнения использую такой код (код работал, но потом я как раз стал переходить на шаблоны :))

template <bool isReduced = false>
    friend bool operator == (const CFraction& val1, const CFraction& val2)
    {
        return (CFraction<true>(CFraction(val1) - CFraction(val2)).m_numenator == 0);
    }

тут вопрос такой - а как показать в параметрах метода, что параметр шаблона у этих параметров могут быть любые, меня они не интересуют, т.е.

friend bool operator == (const CFraction<any>& val1, const CFraction<any>& val2)

иначе это будет восприниматься как isReduced = false, а этого хотелось бы избежать

Еще раз оговорю - решил делать через шаблоны, поскольку сущности дробей сократимых и несократимых одинаковы и отличия лишь в конвертации одних в другие после завершения операций

Answer 1

А зачем вообще у вас при конструкторах?

template <bool isReduced = false>
class CFraction
{
public:
    int     m_numenator;
    int     m_denumenator;
public:
    // конструкторы
    CFraction()
        : m_numenator(0), m_denumenator(1)
    {}
    CFraction(const int numenator)
        : m_numenator(numenator), m_denumenator(1)
    {}
    CFraction(const int numenator, const int denumenator)
        : m_numenator(numenator), m_denumenator(denumenator)
    {}
};

вполне достаточно.

По второй части - см. там же. Просто делаете обычный шаблонный друг

template <bool T>
friend bool operator == (const CFraction<T>& val1, const CFraction<T>& val2)

(Кстати, у вас оператор - не определен, так что тело друга пришлось упростить :))

А что до

const CFraction<> num(1, 3);

то поставьте <>, чтобы было понятно, что это шаблон. Наверное, ваш компилятор несколько староват (или вы не попросили его поддерживать последний стандарт).

"По-моему, так". (с) Пух

Answer 2

В дополнение к ответу @Harry:

Есть два варианта объявления и определения дружественной функции для шаблонного класса:

  1. Мы можем неявно объявить шаблон функции, который должен использовать параметр, отличный от параметра шаблона класса. Данный варинат использован в примере Harry, дублировать его не буду.
  2. Также мы можем предварительно объявить оператор сравнения, следовательно и шаблонный класс CFraction, а уже затем объявить его другом класса.

Пример:

template<bool reduced>
class Fraction;
template<bool F>
auto operator==(const Fraction<F>& f1, const Fraction<F>& f2) -> bool;
template<bool reduced = false>
class Fraction
{
public:
    friend auto operator==<reduced>(const Fraction& f1, const Fraction& f2) -> bool;
};
template<bool F>
inline auto operator==(const Fraction<F>& f1, const Fraction<F>& f2) -> bool
{
    return true;
}

Обратите внимание:

friend auto operator==<reduced>(const Fraction& f1, const Fraction& f2) -> bool;,

я указал <reduced> после оператора. Таким образом мы объявляем специализацию шаблона функции, которая не является членом класса, как друга.

Answer 3

Сначала я выражу свое мнение относительно того, что плохо.

  1. Класс не использует аргумент шаблона, и, впринципе, не сможет использовать воот почему:
  2. Тип известен во время компиляции, и при инстанцировании шаблона, компилятор никак не сможет знать о сокращаемости дробей, потому что это задача времени выполнения.

Во время выполнения нормальный класс должен иметь возможность изменять свои неконстантные члены, так что если дробь изначально был сокращаемым, может стать и не сокращаемым. Чтобы выразить это, лучше применить подход хранения булевского типа в конкретном классе или же воспользоваться преимуществами наследования(первый вариант предпочтительней для данного класса).

Если все же решить впользу шаблона, то это именно тот случай, для которого используется SFINAE

READ ALSO
Как визуализировать прямой поток аудио (интернет радио)?

Как визуализировать прямой поток аудио (интернет радио)?

В интернете много плееров с визуализацией, но все они основаны на статичном mp3 файле, который либо лежит на сервере, либо загружается пользователемЯ...

162
Как отловить браузерный поиск (ctrl+F)?

Как отловить браузерный поиск (ctrl+F)?

Как отловить само событие и получить параметры поиска? Можно ли предотвратить скролл на странице в этот момент?

184
Подгрузка таблицы ajax

Подгрузка таблицы ajax

В таблицу на странице из БД аяксом подгружается таблица вместе с заголовками, как сделать, чтобы заголовок подгружался 1 раз, а не после каждой...

170
Редактируемая таблица на чистом js

Редактируемая таблица на чистом js

Всем добрый деньДелаю динамическую таблицу, с подгрузкой данных из jsone

333