(C ++ 11) Есть ли способ ограничить область действия параметров шаблона для определенного класса c? - PullRequest
0 голосов
/ 28 марта 2020

■ Определение проблемы _________________________

!!You can skip here and directly go to■ Question Summary !!

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

Плюс, я дал ID для каждой черты, которая используется, когда пользователь хочет запрашивать только указанные c черты класса.

Я написал свой собственный класс, удовлетворяющий этим свойствам, используя множественное наследование из неназванных перечислений с variadi c template .

См. ниже:

▼ TriTraits.h

struct TriT
{
    struct Centroid
    {
        Point3D centroid;
        struct ID { enum : utin8_t { CENTROID = 1 }; }; // 0000 0001
    };

    struct Area
    {
        double area;
        struct ID { enum : utin8_t { AREA = 2 }; }; // 0000 0010
    };

    struct Perimeter
    {
        double perimeter;
        struct ID { enum : utin8_t { PERIMETER = 4 }; }; // 0000 0100
    };
    ... // More traits...
};

▼ Triangle.h

#include "TriTraits.h"

enum class TRI_TRAIT_ID : uint8_t {}; // strong type
template<class... Traits>
struct TriangleT : Traits...
{
    struct IDs : Traits::ID...
    {
        enum : uint8_t {
            NONE = 0, // 0000 0000
            ALL = 255 // 1111 1111
        };
    };
    void ComputeTrait(TRI_TRAIT_ID _requestedIDs)
    {
        ... // Implementation will be written somehow, using bitwise & operator.
    }
};

Например, если определить собственный тип треугольника, MyTri<TriT::Area, TriT::Perimeter> myTri, то, что видит компилятор, может выглядеть как это:

struct MyTri
{
    double area;
    double perimeter;

    struct IDs // Since enum can't be inherited, it has 3 individual unnamed enums in it
    {
        enum : uint8_t { AREA = 2 };
        enum : uint8_t { PERIMETER = 4 };
        enum : uint8_t {
            NONE = 0,
            ALL = 255
        };
    };
} myTri;

Этот пользовательский тип треугольника будет вычислять некоторые из его черт, вызывая ComputeTraits(...) с побитовым |operator, например:

myTri.ComputeTraits(MyTri::IDs::AREA | MyTri::IDs::PERIMETER); // Compute area and perimeter

Проблема возникает здесь : предположим, есть Rectangle.h и Pentagon.h, ... NthPolygon.h, таким же образом.

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

myTri.ComputeTraits(MyTri::IDs::AREA | MyRect::IDs::PERIMETER); // Mixed traits of a triangle and of  a rectangle.

Поэтому я хочу перегрузить |operator, что занимает только безымянное перечисление с в каждой области действия NthPolygon::IDs::....

Я пытался написать перегруженный шаблонный оператор внутри private области действия NthPolygon::IDs::..., но это не удалось, так как перегрузка оператора в классе всегда принимает первый параметр как сам по себе: ссылка

Не удалось также сделать его friend глобальной области видимости, поскольку для каждого N-го класса Polygon будет перегружено более 1 |operator с.

enum class N_TH_POLYGON_TRAIT_ID : uint8_t {}; // strong type
struct NthPolygon
{
    ...
    struct IDs
    {
        ...
     private:
        template<typename TRAIT_ID1, typename TRAIT_ID2>
        N_TH_POLYGON_TRAIT_ID operator|(TRAIT_ID1 _id1, TRAIT_ID2 _id2) // Error : too many operators for this operator function
        { return static_cast<N_TH_POLYGON_TRAIT_ID>(static_cast<utin8_t>(_id1) | static_cast<utin8_t>(_id2)); }

        template<typename TRAIT_ID1, typename TRAIT_ID2>
        friend N_TH_POLYGON_TRAIT_ID operator|(TRAIT_ID1 _id1, TRAIT_ID2 _id2) // Error : more than 1 operator "|" matches these operands
        { return static_cast<N_TH_POLYGON_TRAIT_ID>(static_cast<utin8_t>(_id1) | static_cast<utin8_t>(_id2)); }
    };
} myTri;

■ Краткое изложение вопросов _________________________

Как можно составить перегруженная табличка operator| принимает только параметры (безымянные перечисления) из указанного класса c?

enum class STRONG_TYPE_TRI_TRAIT_ID : uint8_t {};
struct Triangle
{
    struct IDs {
        enum : utin8_t { A = 1 };
        enum : utin8_t { B = 2 };
        enum : utin8_t { C = 3 };
    };
};
template<typename TRI_TRAIT_ID1, typename TRI_TRAIT_ID2>
STRONG_TYPE_TRI_TRAIT_ID operator|(TRI_TRAIT_ID1 _id1, TRI_TRAIT_ID2 _id2)
{ return static_cast<STRONG_TYPE_TRI_TRAIT_ID>(static_cast<uint8_t>(_id1) | static_cast<uint8_t>(_id2)); }
enum class STRONG_TYPE_RECT_TRAIT_ID : uint8_t {};
struct Rectangle
{
    struct IDs {
        enum : utin8_t { A = 1 };
        enum : utin8_t { B = 2 };
        enum : utin8_t { C = 3 };
    };
};
template<typename RECT_TRAIT_ID1, typename RECT_TRAIT_ID2>
STRONG_TYPE_RECT_TRAIT_ID operator|(RECT_TRAIT_ID1 _id1, RECT_TRAIT_ID2 _id2)
{ return static_cast<STRONG_TYPE_RECT_TRAIT_ID >(static_cast<uint8_t>(_id1) | static_cast<uint8_t>(_id2)); }
int main(void)
{
    Triangle::IDs::A | Triangle::IDs::B;  // OK
    Triangle::IDs::A | Rectangle::IDs::B; // Error
    ...
    return 0;
}

1 Ответ

1 голос
/ 28 марта 2020

Не уверен, что он удовлетворяет вашим требованиям, но вы можете сделать что-то вроде:

template <typename Tag>
struct TriT
{
    enum class ID_t : uint8_t {};
    friend ID_t operator | (ID_t lhs, ID_t rhs)
    {
        return ID_t(uint8_t(lhs) | uint8_t(rhs));
    }

    struct Centroid
    {
        Point3D centroid;
        struct ID { static const ID_t CENTROID = ID_t(1); }; // 0000 0001
    };

    struct Area
    {
        double area;
        struct ID { static const ID_t AREA = ID_t(2); }; // 0000 0010
    };

    struct Perimeter
    {
        double perimeter;
        struct ID { static const ID_t PERIMETER = ID_t(4); }; // 0000 0100
    };

};

template<typename TRI_TRAIT_ID, class... Traits>
struct TriangleT : Traits...
{
    struct IDs : Traits::ID...
    {
        static constexpr TRI_TRAIT_ID NONE = TRI_TRAIT_ID(0);
        static constexpr TRI_TRAIT_ID ALL = TRI_TRAIT_ID(255);
    };
    void ComputeTrait(TRI_TRAIT_ID _requestedIDs)
    {
        // Implementation will be written somehow, using bitwise & operator.
    }
};

При использовании:

struct TriTag;
struct RectTag;

using MyTri = TriangleT<TriT<TriTag>::ID_t, TriT<TriTag>::Area, TriT<TriTag>::Perimeter>;
using MyRect = RectT<TriT<RectTag>::ID_t, TriT<RectTag>::Area, TriT<RectTag>::Perimeter>;

int main()
{
    MyTri myTri;

    myTri.ComputeTrait(MyTri::IDs::AREA | MyTri::IDs::PERIMETER);
    //myTri.ComputeTrait(MyTri::IDs::AREA | MyRect::IDs::PERIMETER); // error as expected
}

Демо

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...