Вспомогательная структура для предоставления члена данных для общественности

поэтому я пытаюсь создать класс с помощью вспомогательного метода, а именно:

class Type{
    int a, b, c;

    friend auto helper(auto);
    friend auto test_helper(auto);
    /* couples test with implement */

public:
    void method(){
        helper(this);
    }
};

Но превращение helper в функцию друга связывает тест с реализацией, если мы хотим протестировать helper.

Поэтому я хочу сделать helper бесплатной функцией, а именно:

auto helper(int&,int&,int&);
auto test_helper(int&,int&,int&);

class Type{
    int a, b, c;
public:
    void method(){
        helper(a,b,c);
    }
};

Это, однако, делает код намного более утомительным, когда элементов данных много. Итак, мне пришла в голову идея создать вспомогательную структуру с точным элементом данных, как Type, но со всеми элементами данных, которые являются общедоступными, чтобы мы могли просто передать дескриптор такого HelperType, может быть, что-то вроде:

struct HelperType{
    int a, b, c;
};
auto helper(HelperType* a);
auto test_helper(HelperType* a);
void Type::method(){
    helper(static_cast<HelperType*>(this));
}

Существуют ли какие-либо элегантные подходы к созданию такой структуры HelperType? Например, универсальная оболочка или, возможно, с наследованием?


person Taylor Huang    schedule 08.07.2018    source источник
comment
Лучшее, что я могу придумать, это сделать что-то с std::tuple и пакетом вариативного списка параметров шаблона.   -  person πάντα ῥεῖ    schedule 08.07.2018
comment
Почему бы вам просто не сделать геттер-функции для этих значений?   -  person user7860670    schedule 08.07.2018
comment
@VTT Это потому, что этот элемент данных не должен быть общедоступным, а также мы хотим иметь побочный эффект для элемента данных.   -  person Taylor Huang    schedule 08.07.2018
comment
@πάνταῥεῖ что ты имеешь в виду?   -  person Taylor Huang    schedule 08.07.2018
comment
Вы нарисовали свою проблему, как я считаю, слишком широкими штрихами, но я чувствую, что у вас есть XY проблема. Может помощник слишком сложный? Может быть, это слишком сложно, потому что в вашем дизайне отсутствует буква S в SOLID?   -  person StoryTeller - Unslander Monica    schedule 08.07.2018
comment
@StoryTeller Я считаю, что это моя проблема. И не обязательно передавать много параметров, чтобы они выглядели утомительно. Я также не хочу, чтобы мой вопрос был слишком конкретизирован, что привело бы к некоторой работе.   -  person Taylor Huang    schedule 08.07.2018
comment
@TaylorHuang Я имею в виду, что вы можете попробовать подход к привязке типов переменных параметров к вспомогательной функции, используя метапрограммирование шаблонов и пакет параметров вариативного типа.   -  person πάντα ῥεῖ    schedule 08.07.2018
comment
@TaylorHuang Вот похожая вещь .   -  person πάντα ῥεῖ    schedule 08.07.2018
comment
Вы можете сделать эти геттеры невидимыми для всех и пометить функции, которые должны обращаться к этим геттерам, как друзья. Я также не вижу никаких проблем с побочными эффектами для элементов данных.   -  person user7860670    schedule 08.07.2018
comment
превращение помощника в функцию друга связывает тест с реализацией — вам нужно это для модульных тестов? Если да, возможно, #define private public только в этом тесте было бы приемлемым решением? Это не очень элегантно, но не требует каких-либо модификаций проверенного кода.   -  person joe_chip    schedule 08.07.2018
comment
@joe_chip действительно мне нужно, чтобы для модульного теста это, конечно, работало, но я хотел бы избежать этого   -  person Taylor Huang    schedule 08.07.2018


Ответы (1)


У меня нет простого решения для создания HelperType из Type (только то, что приходит на ум, связано с тяжелым метапрограммированием или использованием макросов). Однако построить Type из HelperType было бы довольно тривиально, используя наследование private. Из Производный класс (cppreference.com):

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

// Could be renamed "TypePrivateMembers" or "TypeData"
struct HelperType{
    int a, b, c;
};

auto helper(HelperType* a);

class Type : private HelperType {
public:
    // a,b,c can be accessed in Type class scope.

    void method(){
        helper(this);
    }
};

Текущая демонстрация

Однако все же из cppreference (выделено мной):

Частное наследование также может использоваться для реализации отношения композиции (подобъект базового класса является деталью реализации объекта производного класса). Использование члена обеспечивает лучшую инкапсуляцию и, как правило, предпочтительнее, если только производный класс не требует доступа к защищенным членам (включая конструкторы) базы, не нуждается в переопределении виртуального члена базы, не нуждается в создании базы до и уничтожен после какого-либо другого базового подобъекта, нуждается в совместном использовании виртуальной базы или должен контролировать строительство виртуальной базы. [...]

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

person Vincent Saulue-Laborde    schedule 08.07.2018