Добавить конструктор преобразования в специализацию шаблона

Задний план

Я реализую специальный контейнер для чисел.

template <typename T>
class VectorBase
{
    // Some constructors...

    friend VectorBase<T> operator+(const VectorBase& a, const VectorBase& b)
    {
        VectorBase<T> res;
        // Addition...
        return res;
    }

    // Additional non-trivial logic...
}

Затем он используется для различных типов, особенно для double и float:

typedef VectorBase<float>    VectorFloat;
typedef VectorBase<double>   VectorDouble;

Теперь я хотел бы использовать такую ​​​​конструкцию

VectorFloat  inFloat;
VectorDouble inDouble;

VectorDouble outDouble = inFloat + inDouble;

Где я хочу гарантировать, что операция сложения выполняется для double объектов, а не для float, чтобы избежать проблем с точностью. Точно так же, как это делается в выражениях на чистом C.

Один из способов добиться этого — сначала преобразовать объект VectorFloat в VectorDouble, а затем выполнить операцию сложения в «двойном мире». Это можно сделать вручную (назначив переменной с плавающей запятой временную двойную единицу) или автоматически компилятором во временную переменную. Насколько мне известно, для этого мне нужен параметрический конструктор или оператор присваивания для VectorDouble, который принимает аргумент типа VectorFloat и выполняет фактическое преобразование.

Вопрос

Можно ли добавить новый параметрический конструктор в специализацию шаблона без необходимости репликации всего кода шаблона для определенного параметра шаблона?

VectorBase<double>::VectorBase<double>(const VectorBase<float> &b) { ... }

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

template<typename T>
class VerySpecialDerivedVector: public VectorBase<T> { ... };

person ivokabel    schedule 17.11.2014    source источник


Ответы (1)


Это можно сделать с помощью SFINAE, например:

template <typename T2,
    typename = typename std::enable_if<std::is_same<double, T>::value
                                       && std::is_same<float, T2>::value>::type>
VectorBase(const VectorBase<T2> &) { /* .. */ }

Живой пример.

но я думаю, что было бы лучше иметь:

template <typename T1, typenale T2>
friend VectorBase<typename std::common_type<T1, T2>::type>
operator+(const VectorBase<T1>& a, const VectorBase<T2>& b)
{
    using T = typename std::common_type<T1, T2>::type;
    VectorBase<T> res;
    // Addition...
    return res;
}
person Jarod42    schedule 17.11.2014
comment
Что это за колдовство? Постараюсь понять, а потом попробую. Спасибо! - person ivokabel; 18.11.2014
comment
@ivokabel: SFINAE для первого. type_traits для второго. - person Jarod42; 18.11.2014
comment
Я решил использовать первое предложение, потому что второе, хотя и прекрасно решает описанную проблему, позже вызывает проблемы. А именно, это вызывает неоднозначность при использовании выражений с базовыми типами, например. float len = ...; VectorBase<float> vec1; VectorBase<float> vec2 = vec1 / len;. - person ivokabel; 01.12.2014
comment
PS: Я также хотел бы проголосовать за ваш ответ, но моя репутация слишком низка, чтобы сделать это. - person ivokabel; 01.12.2014