std::list‹const SomeClass› не может быть определен

Я пытаюсь определить постоянный список постоянных объектов, и я не могу это сделать. Вот мой пример, который отлично компилируется:

#include <string>
#include <list>

class Person { public:
    std::string name;
    Person(const std::string &in_name){name=in_name;}
};

class MyClass { public:
    const std::list</* const */Person> l;
    MyClass(const std::list</* const */Person> &in_l):l(in_l){}
};

int main(int argc, char **argv) {
    Person dave("dave");
    MyClass c(std::list<const Person>(dave));
    return 0;
}

Когда я удаляю комментарии из const в этих двух местах, я получаю следующие ошибки:

In file included from /usr/include/x86_64-linux-gnu/c++/7/bits/c++allocator.h:33:0,
                 from /usr/include/c++/7/bits/allocator.h:46,
                 from /usr/include/c++/7/string:41,
                 from main66.cpp:1:
/usr/include/c++/7/ext/new_allocator.h: In instantiation of ‘class __gnu_cxx::new_allocator<const Person>’:
/usr/include/c++/7/bits/allocator.h:108:11:   required from ‘class std::allocator<const Person>’
main66.cpp:11:53:   required from here
/usr/include/c++/7/ext/new_allocator.h:93:7: error: ‘const _Tp* __gnu_cxx::new_allocator<_Tp>::address(__gnu_cxx::new_allocator<_Tp>::const_reference) const [with _Tp = const Person; __gnu_cxx::new_allocator<_Tp>::const_pointer = const Person*; __gnu_cxx::new_allocator<_Tp>::const_reference = const Person&]’ cannot be overloaded
       address(const_reference __x) const _GLIBCXX_NOEXCEPT
       ^~~~~~~
/usr/include/c++/7/ext/new_allocator.h:89:7: error: with ‘_Tp* __gnu_cxx::new_allocator<_Tp>::address(__gnu_cxx::new_allocator<_Tp>::reference) const [with _Tp = const Person; __gnu_cxx::new_allocator<_Tp>::pointer = const Person*; __gnu_cxx::new_allocator<_Tp>::reference = const Person&]’
       address(reference __x) const _GLIBCXX_NOEXCEPT
       ^~~~~~~

Есть ли способ определить std::list объектов const?


person OrenIshShalom    schedule 16.02.2020    source источник
comment
Я не думаю, что у вас может быть такой список. У вас может быть const std::list<Person> или std::list<const Person *>, но список должен иметь возможность изменять значения людей, чтобы быть контейнером этих значений.   -  person sturcotte06    schedule 16.02.2020
comment
ОП, имейте в виду, что MyClass c(std::list<const Person>(dave)); не делает того, что вы думаете. Это объявление функции, а не определение переменной. Используйте {} вместо (), чтобы заставить его делать то, что вы хотите.   -  person walnut    schedule 16.02.2020
comment
Связанный/дубликат: stackoverflow.com/questions/6954906   -  person walnut    schedule 16.02.2020


Ответы (3)


Контейнеры с поддержкой распределителя, такие как std::list, не могут принимать const типов значений, поскольку Требования к распределителю определяют поведение только для типов cv-unqualified. Это означает, что не гарантируется, что контейнер сможет создавать объекты элементов через интерфейс распределителя, если тип значения имеет квалификацию const или volatile.

Это не проблема, потому что контейнера const достаточно, чтобы гарантировать, что элементы не будут изменены. Если вы обращаетесь к элементу контейнера через ссылку const на контейнер, вы всегда получите только ссылку const на элемент.

Итак, просто используйте const std::list<Person> вместо const std::list<const Person>.

Технически кто-то мог бы const_cast удалить constness из такой ссылки, чтобы иметь возможность изменять элементы, и это, вероятно, было бы законным, то есть не неопределенным поведением, но это то, что пользователь всегда может сделать, просто это вызовет неопределенное поведение с const объекты.

См. также Разрешает ли C++11 vector‹const T›? для получения подробной информации.

person walnut    schedule 16.02.2020
comment
Но тогда элементы не const, поэтому кто-то может использовать const_cast, поэтому большинство оптимизаций не могут быть применены. Это действительно одно и то же? - person Ayxan Haqverdili; 16.02.2020
comment
@Ayxan Да, наверное, это было бы законно. Но пользователь всегда может это сделать. Единственная разница в том, будет ли это работать или будет UB, поэтому я не думаю, что это действительно беспокоит. - person walnut; 16.02.2020
comment
Если это так, то почему явное ограничение на константные типы значений было снято после C++11? Интересно, можно ли использовать список const Person, но с std::allocator<Person>. - person Sam Varshavchik; 16.02.2020
comment
Единственная разница в том, будет ли это работать или будет UB Если это UB, то компилятор может проигнорировать эти случаи и применить соответствующие оптимизации. Если это допустимо, компилятор не может этого сделать. - person Ayxan Haqverdili; 16.02.2020
comment
@SamVarshavchik gcc.godbolt.org/z/vMVwLg std::list must have the same value_type as its allocator и std::list must have a non-const, non-volatile value_type - person Ayxan Haqverdili; 16.02.2020
comment
@SamVarshavchik Я не думаю, что были какие-то ограничения конкретно против типов const? Удаление требования CopyAssignable, вероятно, больше связано, например, с. классы с const или эталонными членами, но не с квалификацией высшего уровня const. Я не думаю, что использование другого типа распределителя будет работать, потому что требования к распределителю требуют, чтобы распределитель работал только с T*, а не с const T*. - person walnut; 16.02.2020
comment
@SamVarshavchik Более конкретно, существует явное требование, чтобы value_type распределителя и value_type контейнера были идентичны: en.cppreference.com/w/cpp/named_req/AllocatorAwareContainer - person walnut; 16.02.2020
comment
@Ayxan Я думаю, что на самом деле нечего оптимизировать, потому что пользователь также может размещать в хранилище новые совершенно разные объекты, даже если это было const. Это запрещено только для объектов длительности нединамического хранения: eel.is/c+ +черновик/базовый#life-10 - person walnut; 16.02.2020

std::list должен иметь неконстантный, энергонезависимый value_type.

person Szymon Janora    schedule 16.02.2020

Сообщение об ошибке кажется достаточно ясным https://gcc.godbolt.org/z/MG3Kxv:

ошибка: статическое утверждение не удалось: std::list должно иметь неконстантное, энергонезависимое value_type

person Ayxan Haqverdili    schedule 16.02.2020