Как применить продвижение арифметического типа в конструкторе - PullRequest
1 голос
/ 08 июня 2019

Гипотетически, если бы я создавал класс точек, и я хотел, чтобы он выводил тип на основе аргументов, я бы хотел, чтобы класс точек был переведен в самый высокий аргумент. Например:

template <class dtype>
class Point;

...

auto x = Point(1, 1.0); // Point<double> specialized
auto y = Point(1.0, 1); // Point<double> specialized

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

Вот моя попытка:

#include <type_traits>

template <typename... Ts>
struct promoted_type_wrap;

template <typename T>
struct promoted_type_wrap<T> {
  using type = T;
};

template <typename T, typename U, typename... Ts>
struct promoted_type_wrap<T, U, Ts...> {
  using type = typename promoted_type_wrap<typename std::conditional<
    (sizeof(U) <= sizeof(T)), T, U >::type, Ts... >::type;
};

template <typename... Ts>
using promoted_type = typename promoted_type_wrap<Ts...>::type;

template <typename T>
using same_type = typename promoted_type_wrap<T>::type;

template <class dtype>
class Point {
protected:
  dtype x, y;

public:
  constexpr Point(const dtype x, const same_type<dtype> y)
    : x(x), y(y) {
  }
};

template <class dtype, class etype>
constexpr auto make_Point(const dtype x, const etype y) {
  return Point<promoted_type<dtype, etype>>(x, y);
}

void test() {
  constexpr auto x = make_Point(1, 2.0); // Point<double> specialized
  constexpr auto y = make_Point(1.0, 2); // Point<double> specialized
  constexpr auto z = Point(1, 2.0); // Point<int> specialized
  constexpr auto w = Point(1.0, 2); // Point<double> specialized
}

Имеет смысл, почему Point(1, 2.0) специализируется как Point<int>, так как первый аргумент является int, который заставляет второй аргумент в конструкторе как int; однако я не уверен, как переписать конструктор, чтобы он вел себя как фабрика псевдоконструкторов.

1 Ответ

2 голосов
/ 08 июня 2019

однако я не уверен, как переписать конструктор так, чтобы он вел себя как фабрика псевдоконструкторов.

Не конструктор: вы должны написать собственное руководство по выводам.

Что-то следующее

template <typename T1, typename T2>
Point(T1, T2) -> Point<promoted_type<T1, T2>>;

Ниже приведен полный пример компиляции

#include <type_traits>

template <typename... Ts>
struct promoted_type_wrap;

template <typename T>
struct promoted_type_wrap<T>
 { using type = T; };

template <typename T, typename U, typename... Ts>
struct promoted_type_wrap<T, U, Ts...>
 { using type = typename promoted_type_wrap<std::conditional_t<
      (sizeof(U) <= sizeof(T)), T, U >, Ts... >::type; };

template <typename... Ts>
using promoted_type = typename promoted_type_wrap<Ts...>::type;

template <typename dtype>
class Point
 {
   protected:
      dtype x, y;

   public:
      template <typename T1, typename T2>
      constexpr Point (T1 const & a, T2 const & b) : x(a), y(b)
       { }
 };

template <typename T1, typename T2>
Point(T1, T2) -> Point<promoted_type<T1, T2>>;

int main ()
 {
   constexpr auto z = Point(1, 2.0); // now Point<double>
   constexpr auto w = Point(1.0, 2); // again Point<double> 

   static_assert( std::is_same_v<decltype(z), Point<double> const> );
   static_assert( std::is_same_v<decltype(w), Point<double> const> );
 }

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

template <typename T, typename U, typename... Ts>
struct promoted_type_wrap<T, U, Ts...>
 { using type = typename promoted_type_wrap<std::conditional_t<
      (sizeof(U) <= sizeof(T)), T, U >, Ts... >::type; };

Даже игнорируя другие проблемы, которые возникают, если у вас разные типы одинакового размера, выбранный тип является первым.

Например, в моей платформе g ++ и clang ++ имеют sizeof(long) == sizeof(float), поэтому мы получаем

constexpr auto z = Point(1l, 2.0); // <-- deduced as Point<long>
constexpr auto w = Point(1.0, 2l); // <-- deduced as Point<double>

static_assert( std::is_same_v<decltype(z), Point<long> const> );
static_assert( std::is_same_v<decltype(w), Point<double> const> );

Я предлагаю использовать что-то, что выбирает «предпочтительный тип» независимо отпорядок типов.

Мне кажется, что вы должны использовать std::common_type следующим образом

#include <type_traits>

template <typename dtype>
class Point
 {
   protected:
      dtype x, y;

   public:
      template <typename T1, typename T2>
      constexpr Point (T1 const & a, T2 const & b) : x(a), y(b)
       { }
 };

template <typename T1, typename T2>
Point(T1, T2) -> Point<std::common_type_t<T1, T2>>;

int main ()
 {
   constexpr auto z = Point(1l, 2.0); // <-- deduced as Point<double>
   constexpr auto w = Point(1.0, 2l); // <-- deduced as Point<double>

   static_assert( std::is_same_v<decltype(z), Point<double> const> );
   static_assert( std::is_same_v<decltype(w), Point<double> const> );
 }
...