#c #c 11 #list-initialization
#c #c 11 #список-инициализация
Вопрос:
Я знаю, что попытка использовать a std::initializer_list<NonCopyable>
приводит к ошибке, потому что элементы копируются во временный массив, представленный initializer_list
. Я также прочитал некоторое объяснение того, почему было бы неправильно иметь ссылки на rvalue в списке, с чем меня все устраивает.
Проблема в том, что я хотел бы передавать не копируемые объекты не для того, чтобы перейти от них, а только const
для доступа к ним, поэтому аргумент о rvalues неприменим. Что я могу сделать, чтобы сохранить, по возможности, синтаксис инициализации списка и ссылочную семантику (без оболочек, без необработанных указателей)?
NonCopyable a{...}, b{...};
ListInitialized c{a, b};
Я думаю, что здесь я упускаю что-то чрезвычайно очевидное.
Обновить:
Это работает (*),
ListInitialized(std::initializer_list<std::reference_wrapper<NonCopyable>>) {...}
но не будет принимать rvalues. Было бы неплохо, если бы я мог просто передать список всего, что могло бы войти в const NonCopyableamp;
.
(*) Я знаю, что написал «без оболочек», но это не влияет ни на вызывающий код, ни на итерацию по списку.
Комментарии:
1. Возможно, я что-то неправильно понял, но почему не просто
std::initializer_list<std::reference_wrapper<const NonCopyable>>
?2. @MichaelKenzel
const
или нет, он не будет принятListInitialized c{NonCopyable{...}}
. Если быT
можно было копировать, я мог бы свободно использовать существующие объекты типаT
или сразу созданные, или смешивать и сопоставлять.3. Действительно, я забыл, что это
std::reference_wrapper
мешает вам использовать его со ссылками на rvalue. Вы могли бы запустить свою собственную вспомогательную функцию преобразования, например,std::reference_wrapper<const T> cref(Tamp;amp; r) { return r; }
а затем выполнитьListInitialized { cref(NonCopyable{...}) }
. Но я бы, вероятно, просто выбрал подход, описанный Angew ниже…
Ответ №1:
Вы можете предоставить ListInitialized
шаблон конструктора с переменными значениями:
struct ListInitialized
{
template <class... T>
ListInitialized(const T... amp;arg);
};
Если вам нужно убедиться, что его экземпляр может быть создан только с правильным типом, рассмотрите подходящие SFINAE:
struct ListInitialized
{
template <
class... T,
class Sfinae = std::enable_if_t<std::is_same<std::decay_t<T>, NonCopyable> amp;amp;...
>
ListInitialized(const T... amp;arg);
};
Комментарии:
1. Хороший подход! Это каким-то образом противоречит намерению
std::initializer_list
, хотя?2. Я думаю, что это прекрасно подойдет в моем случае использования. Я просто боюсь, что цена, возможно, будет заключаться в большом количестве разных экземпляров, сгенерированных, если количество элементов имеет тенденцию меняться, поэтому я подожду, есть ли другая альтернатива.
Ответ №2:
В дополнение к комментариям и ответам выше, я обнаружил, что эта минималистичная оболочка удовлетворяет моим потребностям:
#include <initializer_list>
#include <utility>
struct S {
S() { }
S(const Samp;) = delete; // Non-copyable
void f() const { }
};
template<class T>
class const_reference_wrapper {
public:
const_reference_wrapper(const Tamp; ref_) : ref(ref_) { }
operator const Tamp;() const { return ref; }
private:
const Tamp; ref;
};
struct T {
T(std::initializer_list<const_reference_wrapper<S>> l) : c(l.size()) {
for(const Samp; i : l) // note: const autoamp; can't be used here, but it would be the same for std::reference_wrapper
i.f(); // we can do something with the elements
}
int c;
};
int main() {
S a, b;
T t{a, b, S{}}; // we can mix lvalues and rvalues with a natural syntax
return t.c; // correctly returns 3
}
Конечно, необходимо позаботиться о том, чтобы любое rvalue, переданное через это, сохранялось в течение всего времени, когда на него ссылаются.