Это звучит как основной вопрос, но я не нашел исчерпывающего ответа, так что вот оно. Рассмотрим этот фрагмент кода:
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
, или оно может считаться слишком конкретным?