Хранение ссылки const на объект в классе

Это звучит как основной вопрос, но я не нашел исчерпывающего ответа, так что вот оно. Рассмотрим этот фрагмент кода:

struct A {
    const std::string& s;
    A(const std::string& s) : s(s) {}
};

int main() {
    A a("abc");
    std::cout << a.s << std::endl;
    return 0;
}

Демо.

Пока я понимаю, это UB. Строковый литерал "abc" связывается с const std::string& в конструкторе, создавая временный объект строки. Он также привязан к a.s, и он разрушается после построения a. То есть константная ссылка не может продлить продолжительность жизни. Висячая ссылка, бум. В этом конкретном случае я вообще не вижу вывода на ideone.com, но что-то может произойти (помните cyciraptors).

Хорошо, это ясно. Но что, если это на самом деле наше намерение: мы хотим сохранить константную ссылку на объект? К существующему, а не к временному? Это звучит как очень естественная задача, но я придумал только одно (почти) естественное решение. Принятие аргумента конструктора std::reference_wrapper вместо ссылки:

    A(std::reference_wrapper<const std::string> r) : s(r) {}

Так как std::reference_wrapper удалил конструкторы из временных рядов:

reference_wrapper( T&& x ) = delete;

это работает так же, как и ожидалось. Однако это не совсем элегантно. Еще один подход, который я могу придумать, - принять ссылку на отправку T&& и отклонить все, кроме строк const l-value, с помощью std::enable_if. Думаю, это еще менее изящно.

Любые другие подходы?

UPD Другой вопрос: это законное использование std::reference_wrapper, или оно может считаться слишком конкретным?

Ответ 1

Я бы сказал, что естественным решением было бы сделать то, что reference_wrapper делает: предотвратить конструкцию из временных:

struct A {
    const std::string& s;
    A(const std::string& s) : s(s) {}
    A(std::string&&) = delete;
};

Вы также должны иметь в виду, что наличие члена данных ссылочного типа делает класс не назначаемым (даже назначение переноса возможно) по умолчанию, и обычно сложно реализовать оператор присваивания. Вы должны рассмотреть возможность хранения указателя вместо ссылки:

struct A {
    const std::string* s;
    A(const std::string& s) : s(&s) {}
    A(std::string&&) = delete;
};