Используйте std :: reference_wrapper <const T> для обработки const T & в конструкторе - подходит? - PullRequest
0 голосов
/ 16 января 2019

Классу принадлежит объект типа U. Через метод он выставляет этот объект как const U&, используя геттер (не дешевое копирование, модификация не желательна).

Теперь клиент хочет использовать этот API. Он хочет использовать экземпляр U как часть сложного объекта (который не заинтересован в изменении объекта API). Поэтому у него есть как минимум следующий вариант: Создайте класс T с const U& в качестве параметра и закрытое поле типа const U&, где конструктор хранит экземпляр API. Это имеет крайний недостаток, заключающийся в том, что экземпляры класса становятся чрезвычайно негибкими (например, нет управления с использованием std :: vectors), что нежелательно.

Не так давно я обнаружил, что можно также использовать std::reference_wrapper<const U> для хранения const U&, что не налагает этих недостатков на экземпляры типа T.

Вопрос теперь в том, ведет ли себя это так, как ожидается, и является ли это хорошей практикой?

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

#include <iostream>
#include <memory>

class U{
    public: 
    uint value;
};

class T{
private:
    std::reference_wrapper<const U> _u;

public:
    T(const U& u)
        :_u(u) {}

    const U& GetU(){
        return _u;
    }
};

const U& provideValue(U& u){
    return u;
}

int main()
{
    U internalApiValue;
    internalApiValue.value = 5;

    auto apiValue = provideValue(internalApiValue);

    T container(apiValue);

    std::cout << container.GetU().value;
}

Полагаю, если это не очень хорошая идея, единственной альтернативой было бы избегать использования const, поскольку в противном случае я бы наложил высокие ограничения на пользователей таких методов (методы, предоставляющие const U& вместо U& или U)

Ответы [ 2 ]

0 голосов
/ 16 января 2019

Одной из основных проблем вашего интерфейса является то, что единственный конструктор T занимает const U&. Это означает, что вы можете передать временное значение в T и остаться с reference_wrapper для мертвого объекта, поскольку const& в объекте не продлевает время жизни временного объекта.

Чтобы решить эту проблему, вам нужно добавить удаленный конструктор, который останавливает вас от принятия временных. Добавление

T(const U&&) = delete;

сделает это.

0 голосов
/ 16 января 2019

Это должно делать то, что вы хотите. Он использует std::reference_wrapper<T> так, как это было задумано (передача ссылок способом, который делает их копируемыми и назначаемыми). Я не вижу в этом ничего плохого. От cppreference.com :

std :: reference_wrapper - шаблон класса, который оборачивает ссылку в копируемый, назначаемый объект. Он часто используется как механизм для хранения ссылок внутри стандартных контейнеров (например, std :: vector), которые обычно не могут содержать ссылки.

Единственный потенциальный недостаток, который я вижу, это то, что std::reference_wrapper<T> может быть немного неудобным для использования и незнакомым для некоторых. Более распространенным решением вашей проблемы, вероятно, было бы просто сохранить указатель в вашем объекте вместо ссылки. Например:

class T {
private:
    const U* _u;

public:
    T(const U& u)
        : _u(&u) {}
    …
};
...