Как сделать базовые классы Variadic CRTP друзьями производного класса

Основная часть проблемы заключается в использовании CRTP с дизайном на основе политик и вариативным шаблоном. Из политики не удается получить доступ к защищенным или закрытым членам основного/производного класса. Из-за использования вариативного шаблона я не могу объявлять политики просто друзьями.

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

Учитывая это решение CRTP, что поддерживает несколько уровней наследования и решает проблемы с алмазами без виртуального наследования.

// Derived     - We would like to obtain access to this type of instance
// BaseDerived - Helper type to avoid the diamond problem without virtual inheritance
template<typename Derived, template<typename> class BaseDerived>
class Crtp {
protected:
    [[nodiscard]] constexpr Derived & underlying() noexcept
    {
        return static_cast<Derived &>(*this);
    }

    [[nodiscard]] constexpr Derived const & underlying() const noexcept
    {
        return static_cast<Derived const &>(*this);
    }
};

// Helper struct to achive multiple inheritance
struct NoDerivedClassTag;

template<template<typename> class Derived, typename Substitute, template<typename> class Base>
using DerivedCrtpBase = Base<std::conditional_t<std::is_same_v<Substitute, NoDerivedClassTag>, Derived<NoDerivedClassTag>, Substitute>>;

template<template<typename> class Interface, typename Object>
using is_crtp_interface_of = std::enable_if_t<
    std::is_same_v<Interface<NoDerivedClassTag>, Object> || std::is_base_of_v<Interface<typename Object::exact_type>, Object>>;

Использование этого решения CRTP в дизайне на основе политик с вариативным шаблоном, подобным этому

template<template<typename> class... Functionality>
class FinalDerived
    : public Functionality<FinalDerived<Functionality...>>...
{
public:
    constexpr int get() const
    {
        return protected_variable_;
    }

// Remove to check the problem
//protected:
    int protected_variable_ {-1};
};

Цель состоит в том, чтобы использовать защищенную переменную из политики, подобной этой.

template<typename Derived>
struct Increment
    : Crtp<Derived, Increment>
{
    void increment(int an_value)
    {
        this->underlying().protected_variable_ += an_value;
    }
};

template<typename Derived>
struct Decrement
    : Crtp<Derived, Decrement>
{
    void decrement(int an_value)
    {
        this->underlying().protected_variable_ -= an_value;
    }
};

Пример использования

constexpr int number {7};

int main(void){
    FinalDerived<Increment, Decrement> derived;

    std::cout << "start: " << derived.get() << "\n";

    derived.increment(number);
    std::cout << "incremented: " << derived.get() << "\n";

    derived.decrement(number);
    std::cout << "decremented: " << derived.get() << "\n";
}

Пример запуска


person István Simon    schedule 27.04.2020    source источник
comment
Связанный вопрос: stackoverflow.com/questions/31510844/   -  person Evg    schedule 27.04.2020
comment
В настоящее время не поддерживаются друзья с переменным числом аргументов.   -  person Jarod42    schedule 27.04.2020


Ответы (2)


Решение, которое я могу придумать, состоит в том, чтобы использовать классы признаков, унаследованные классами, которые вы создаете, и определенные до вашей базы и политик CRTP, чтобы они были полными классами. Класс трейтов может содержать указатель или ссылку на нужный член, объявления типов, общие для классов с этими трейтами и т. д., посмотрите, как это делается в стандартных компонентах.

person Swift - Friday Pie    schedule 27.04.2020
comment
Можете ли вы привести небольшой пример, как решить проблему? Спасибо - person István Simon; 27.04.2020

Как написал Evg, есть старый вопрос с правильным ответом.

#ifndef CRTP_VARIADIC_FRIEND

// M - Macro
// N - Number
// P - Packed parameters
// T - Type
#    define CRTP_FRIEND_REPEAT_2(M, N, P, T) M(N, P, T) M(N + 1, P, T)
#    define CRTP_FRIEND_REPEAT_4(M, N, P, T) CRTP_FRIEND_REPEAT_2(M, N, P, T) CRTP_FRIEND_REPEAT_2(M, N + 2, P, T)
#    define CRTP_FRIEND_REPEAT_8(M, N, P, T) CRTP_FRIEND_REPEAT_4(M, N, P, T) CRTP_FRIEND_REPEAT_4(M, N + 4, P, T)
#    define CRTP_FRIEND_REPEAT_16(M, N, P, T) CRTP_FRIEND_REPEAT_8(M, N, P, T) CRTP_FRIEND_REPEAT_8(M, N + 8, P, T)
#    define CRTP_FRIEND_REPEAT_32(M, N, P, T) CRTP_FRIEND_REPEAT_16(M, N, P, T) CRTP_FRIEND_REPEAT_16(M, N + 16, P, T)
#    define CRTP_FRIEND_REPEAT_64(M, N, P, T) CRTP_FRIEND_REPEAT_32(M, N, P, T) CRTP_FRIEND_REPEAT_32(M, N + 32, P, T)
#    define CRTP_FRIEND_REPEAT_128(M, N, P, T) CRTP_FRIEND_REPEAT_64(M, N, P, T) CRTP_FRIEND_REPEAT_64(M, N + 64, P, T)

#    define CRTP_FRIEND(N, P, T) \
        friend std::tuple_element_t<std::min(static_cast<std::size_t>(N + 1), sizeof...(P)), std::tuple<void, T...>>;

#    define CRTP_VARIADIC_FRIEND(P, T) CRTP_FRIEND_REPEAT_128(CRTP_FRIEND, 0, P, T)

#endif

Я попробую решение класса Trait. Позже будет другой ответ.

Запускаемый пример

person István Simon    schedule 28.04.2020