Специализация оператора шаблона () для особого типа?

#c #templates #visual-c #c 17 #c 14

Вопрос:

У меня есть векторный класс, основанный на шаблонах. У меня есть шаблон преобразования единиц измерения, основанный на анализе измерений Скотта Мейерса в C . Я не собираюсь перечислять это, поскольку это сложно, если только проблема не может быть решена без этих деталей. Я использую оператор приведения, когда мне нужно передать данные внешним функциям.

 template<typename T>
struct XYZVector 
{
  operator Point3D() { return Point3D(x, y, z); }
}
 

Если я использую T как double, он работает нормально. Но когда я это делаю, мне нужна специализация.

 Point3D vecOut = XYZVector<Unit::usFoot>(x,y,z);
 

Произойдет сбой, потому что при преобразовании объекта Unit вам нужно использовать метод as() .

 operator Point3D() { return Point3D( x.as<T>(), y.as<T>(), z.as<T>() ); }
 

Итак, мне нужно как-то сказать, что если T является единицей, используйте эту строку, в противном случае используйте следующую строку.

 template<typename T>
struct XYZVector
{
  //if typename T is a Unit type use this
  operator Point3D() { return Point3D( x.as<T>(), y.as<T>(), z.as<T>() ) }
  //otherwise use this
  operator Point3D() { return Point3D(x, y, z); }
}
 

Возможно ли это?

Ответ №1:

if constexpr на помощь.

 operator Point3D() { 

     if constexpr (std::is_same_v<std::remove_cvref_t<T>,Unit>){
          return Point3D(x.as<T>(),y.as<T>(),z.as<T>());
     }else{
          return Point3D(x,y,z);
     }
}
 

Комментарии:

1. Общая идея хорошая, но код не совсем правильный. Unit это не тип. В идеале это должно быть обработано с помощью концепции, но is_unit_type<T>::value тип признака был бы более обратно совместим.

2. @Frank, приведенный Staz пример не сработал для меня, и я думаю, что вы намекали на это и предлагали лучший способ. Но я не уверен, как это реализовать. Не могли бы вы привести пример? Спасибо

3. @MarkTwombley я вроде как использовал Unit в качестве заполнителя для типа. Вы можете заменить его соответствующим образом. Также std::remove_cvref является C 20 признаком типа. Вы можете использовать std::remove_cv_t<std::remove_reference_t<T>> вместо этого, если ваш стандарт cpp ниже 20.

4. Спасибо @Staz за это обновление. У меня это работает.

Ответ №2:

Я бы использовал перегрузки в качестве точки настройки:

 // overload for built-in type (no ADL)
double to_double(double d) { return d;}

template <typename T>
struct XYZVector
{
    // ...
    operator Point3D() const {
        return Point3D(to_double(x),
                       to_double(y),
                       to_double(z));
    }
};

// The ones which can be found by ADL can be placed after
namespace Unit {
    double to_double(usFoot u) { return u.as<double>(); } 
}