Выбор конструктора во время компиляции

Я пытаюсь реализовать следующий API:

geojson::position_t<> point_1{ 10, 12 };
geojson::position_t<> point_2( 11, 13 );
geojson::position_t<> point_3(std::pair<int, int>(12, 14));

geojson::position_t<> line_1{ 
   geojson::position_t<>{ 100, 120 }, 
   geojson::position_t<>{ 110, 130 } };

geojson::position_t<> poly_1{
  { geojson::position_t<>{ 100, 120 }, geojson::position_t<>{ 110, 130 }}, 
  { geojson::position_t<>{ 101, 121 }, geojson::position_t<>{ 111, 131 }} };

Идея состоит в том, чтобы иметь класс шаблона position_t <> со следующими свойствами:

  • иметь некоторый внутренний value_type, определяющий, является ли это точечным или линия
  • иметь конструкторы и использовать SFINAE, чтобы определить, является ли точечный или line-type может быть создан на основе параметров, переданных в ctor
  • есть метод value_type get() const {...}, возвращающий точку или тип линии в зависимости от того, был ли вызван точечный или линейный ctor

Мой первый подход заключался в использовании boost :: variant, но я застрял в том, как получить value_type и реализовать метод get().

Вторая попытка заключалась в использовании частичной специализации шаблона. Но пока у меня не получилось.

Может ли кто-нибудь предложить подход, как получить требуемый API?

Почему вы хотите, чтобы это был один класс, вы хотите хранить его в контейнере std?

Yola 21.03.2018 09:29

Это невозможно. Если value_type находится во время компиляции, вам нужно получить 2 разных класса.

llllllllll 21.03.2018 09:44

Хотел бы API быть таким? Обычно такие вещи реализуются как класс Point и абстрактный класс Shape, хранящий контейнер точек, а также классы Line, Rect и т. д., Унаследованные от Shape.

Andrew Kashpur 21.03.2018 09:45

Почему вы хотите, чтобы один тип в разных обстоятельствах вел себя так, как если бы он принадлежал к нескольким типам?

Peter 21.03.2018 09:45

Что ж, может у меня действительно неправильный дизайн и требования. Я не хотел реализовывать полиморфные классы времени выполнения и хотел добиться результата, используя правила вывода типа SFINAE /.

drus 21.03.2018 09:52

Можете ли вы использовать C++ 17? Затем вы можете использовать вычет для position_t<point> по сравнению с position_t<line>.

Jarod42 21.03.2018 09:56

Да, C++ 17 мне подходит.

drus 21.03.2018 10:05
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
7
100
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Вот два дополнительных решения:

Первый использует стандартный полиморфизм, он использует базовый класс GraphicBase. Второй подход - использовать std :: variant для хранения объектов. Если это используется, вы можете удалить базовый класс и виртуальные функции. Как видно из вашего комментария, вы видите в правилах вывода типа решение. Да, мой пример работает именно так!

У него есть некоторые плюсы и минусы для полиморфизма vtable по сравнению с помеченным объединением как std :: variant. И то, и другое возможно!

Замечание по поводу:

have some internal value_type identifying whether it is point or line

Вам не нужен какой-либо дополнительный идентификатор, потому что сам вариант уже содержит этот тег типа. Вариант - это просто объединение и дополнительная запись данных, которая отслеживает фактический присвоенный тип. Именно то, что вы хотите! Вы можете использовать этот тег с std::visit для вызова любой функции, которую соответствующий тип передал переданному вами объекту функции. (В моем примере я использую общую лямбду, чтобы упростить отправку.

Намекать: Не думайте о SFINAE, если возможна простая перегрузка или специализация!

class GraphicBase
{
    public:
        virtual void Print()=0;
        // for using with vtable polymorphism
        GraphicBase* Get() { return this; }
};


class Point: public GraphicBase
{
    public:
        Point( int, int ){}

        void Print() override { std::cout << "Point" << std::endl; }
        void PrintNonVirtual() { std::cout << "Point" << std::endl; }
};

class Line: public GraphicBase
{
    public:
        Line( Point, Point ){}
        void Print() override { std::cout << "Line" << std::endl; }
        void PrintNonVirtual() { std::cout << "Line" << std::endl; }
};


template < typename T >
class Graphics: public T
{
    public: 
        using T::T;

        // for using with variants
        T GetNonVirtual() { return static_cast<T>(*this);}
};  

Graphics( int, int ) -> Graphics<Point>;
Graphics( Point, Point ) -> Graphics<Line>;

int main()
{
    Graphics p1(1,1);
    Graphics l1({1,2},{3,4});

    // using vtable polymorphism
    std::vector<GraphicBase*> v;
    v.push_back( &p1 );
    v.push_back( &l1 );

    for ( auto el: v ) el->Print();

    // using the Get() to get the base pointer
    GraphicBase* ptr;
    ptr = p1.Get();
    ptr->Print();

    ptr = l1.Get();
    ptr->Print();

    // or using variants:
    using VT = std::variant< Point, Line >;

    std::vector<VT> var;
    var.push_back( p1 );
    var.push_back( l1 );

    for ( auto& el: var )
    {
        std::visit( []( auto& v ){ v.PrintNonVirtual(); }, el );
    }

    // here we get a copy of the object ( references not allowed in variants! )
    VT va1 = p1.GetNonVirtual();
    VT va2 = p1.GetNonVirtual();

    std::visit( []( auto& v ){ v.PrintNonVirtual(); }, va1 );
    std::visit( []( auto& v ){ v.PrintNonVirtual(); }, va2 );
 }
Ответ принят как подходящий

В C++ 17 вы можете использовать выведенное руководство, поэтому у вас есть как position_t<Point>, так и position_t<Line>, и в соответствии с конструктором параметров выберите правильный.

Что-то вроде:

class Point
{
public:
    int x;
    int y;
};

class Line
{
public:
    Point start;
    Point end;
};

template <typename T> class position_t;

template <>
class position_t<Point>
{
public:
    position_t(int x, int y) : point{x, y} {}
    position_t(const std::pair<int, int>& p) : point{p.first, p.second} {}

    const Point& get() const { return point; }
private:
    Point point;  
};

template <>
class position_t<Line>
{
public:
    position_t(const position_t<Point>& start,
               const position_t<Point>& end)
        : line{start.get(), end.get()}
    {}

    const Line& get() const { return line; }
private:
    Line line;  
};

А затем руководство по дедукции

position_t(int, int) -> position_t<Point>;
position_t(std::pair<int, int>) -> position_t<Point>;
position_t(const position_t<Point>&, const position_t<Point>&) -> position_t<Line>;

Так:

geojson::position_t point_1{ 10, 12 }; // geojson::position_t<Point>
geojson::position_t point_2( 11, 13 ); // geojson::position_t<Point>
geojson::position_t point_3(std::pair<int, int>(12, 14)); // geojson::position_t<Point>

geojson::position_t line_1{ // geojson::position_t<Line>
    geojson::position_t{ 100, 120 },   // geojson::position_t<Point>
    geojson::position_t{ 110, 130 } }; // geojson::position_t<Point>

Демо

Например, большое спасибо - это то, что я искал.

drus 21.03.2018 14:51

Другие вопросы по теме