Мне любопытно, знает ли кто-нибудь, почему g++
компилирует приведенный ниже код, а clang++
выдает ошибку. Код создает std::map<int, int, SortCriterion>
с пользовательским функтором сортировки SortCriterion
. Через конструктор SortCriterion
можно указать тип сортировки: по возрастанию или по убыванию. Сравнение ключей реализовано через operator()(int, int)
. Под g++
все компилируется и работает нормально, даже с -Wall, -Wextra, Wpedantic
и т.д. Однако clang++
выдает ошибку при вызове функции insert
и жалуется на const
-ность оператора сравнения, т.е. хочет operator()(int, int) const
.
note: candidate function not viable: 'this' argument has type
'const SortCriterion', but method is not marked const
bool operator()(int x, int y) //const
Теперь я знаю, что с ключами в ассоциативных контейнерах не следует связываться, так как это повредит внутреннюю структуру контейнера, но обеспечивается ли const
-ность STL? Кажется, что clang++
ожидает std::map<key, value, const comparator>
, тогда как g++
не навязывает const
.
PS: g++4.9
, Apple LLVM version 5.1 (clang-503.0.40)
#include <map>
#include <string>
#include <iostream>
using namespace std;
class SortCriterion
{
bool ascending;
public:
SortCriterion(bool sorting_type): ascending(sorting_type){};
// if operator below is not marked const,
// clang++ spits a compile-time error on insert
// however g++ compiles it ok
bool operator()(int x, int y) //const
{
if(ascending) // ascending
return x<y;
else
return x>y;
}
};
int main()
{
map<int, string, SortCriterion> m(SortCriterion(false)); // ascending
m.insert({1,"one element"}); // clang++ compile error here
m.insert({2,"another element"});
for(auto elem: m)
cout << elem.first << ": " << elem.second << endl;
}
operator()
должен быть помечен какconst
, чтобы иметь возможность использоватьconst
ссылки наmap
s. Тогда я думаю, чтоlibstdc++
есть ошибка. - person vsoftco   schedule 29.07.2014c
в качестве аргумента конструкторуmap
тогдаc
должен иметь типCopyConstructible
и использовать копиюc
в качестве объекта сравнения. Таким образом, на самом деле нет спецификации, что функцияoperator()
должна бытьconst
. - person M.M   schedule 29.07.2014operator()
не применяетсяconst
, то задача состоит в том, чтобы обеспечитьconst
-ность дляconst
ссылок, иначе это не сработает дляconst map&
. Итак, в заключениеg++
прав? Может быть, вы можете опубликовать это как ответ, чтобы в будущем его можно было найти. - person vsoftco   schedule 29.07.2014map
равенconst Compare&
,operator()
также должен бытьconst
, но это неверно.map
может (и будет) делать копию объекта компаратора. Аргументы Керрека о том, что требования различныхmap
функций-членов требуют, чтобыoperator()
компаратора былоconst
, гораздо более убедительны. Например, gcc подавится кодом выше, если вы добавите строкуauto c = m.count(1);
. - person Praetorian   schedule 29.07.2014const Compare &
, не означает, что компаратор должен бытьconst
(однако могут быть другие пункты, которые подразумевают, что компаратор должен быть константным) - person M.M   schedule 29.07.2014operator()
для проверки эквивалентности? - person vsoftco   schedule 29.07.2014map::count
является константной функцией-членом, поэтому, когда она пытается вызвать неконстантнуюoperator()
для вашего компаратора, возникает ошибка. - person Praetorian   schedule 29.07.2014const map&
- я не совсем понимаю, что вы имеете в виду, но это кажется неправильным ;-). Даже сconst map
функции-членыmap
могут создать объектCompare()
, отличный отconst
, и действительно есть функцияkey_compare map::key_comp() const;
, которая явно возвращает объект, отличный отconst
key_compare
, который являетсяtypedef
для параметра шаблонаCompare
. Итак, да,g++
кажется правильным, но, несмотря на это, есть много намеков на то, что, вероятно, ожидалась версияconst
: по умолчаниюCompare
-std::less
- определяет версиюconst
, ... - person Tony Delroy   schedule 29.07.2014map::value_compare
, в которомoperator()
равноconst
. Есть места (Таблица 102), где указано «ИспользуетCompare()
в качестве объекта сравнения» — это тонко намекает на временное значение, что означает, что могут быть вызваны толькоconst
членов. Конечно, есть и тот факт, чтоoperator
должно логически бытьconst
, независимо от того, предписывает это Стандарт или нет. - person Tony Delroy   schedule 29.07.2014map
по ссылкеconst
не позволит вам вызвать его компаратор, отличный отconst
, поэтому логично пометить егоconst
- person vsoftco   schedule 29.07.2014const map
, которую вы вызываете, может свободно кодироватьCompare c; if c(a, b) ...
. (В Стандарте ничего не говорится о том, что объектCompare
map
также не хранится вместе сmutable
, хотя это разумное предположение.) - person Tony Delroy   schedule 29.07.2014const map
может вызывать неконстантный функтор (последний может иметь побочный эффект). Если я попытаюсь сделать то, что вы предлагаете, я получуpassing const Foo<SortCriterion> as this argument of void Foo<Compare>::do_something() [with Compare = SortCriterion] discards qualifiers [-fpermissive] foo.do_something();
- person vsoftco   schedule 29.07.2014do_something
для константного объекта - person M.M   schedule 29.07.2014const
? И если да, то вы не сможете использоватьoperator()
из функтора, если он не помеченconst
. Может быть, я что-то упускаю, извините за слишком много комментариев, но хотелось бы понять, что происходит. - person vsoftco   schedule 29.07.2014const object
, вы не можете использовать не-const
функции элемента данных, отличного отmutable
, но ключевым моментом здесь было то, был ли объектCompare
, используемый для сравнения, обязательно членом данныхmap
, а не его не-const
копия, сделанная в функции-члене карты, желающей выполнить сравнение. В любом случае, я думаю, что ответ, который я опубликовал, показывает еще одно убедительное доказательство того, что версия, отличная отconst
, необходима для работы некоторых функцийmap
. Конечно, с шаблонами компилятор не обязательно проверяет все, что не вызывается. - person Tony Delroy   schedule 29.07.2014{}
для вставки элемента, напримерm.insert({1,"one element"});
, но неstd::make_pair(1,"one element")
? Я не могу скомпилировать это с VS2012, это новый синтаксис С++ 14? - person Marson Mao   schedule 29.07.2014void insert (initializer_list<value_type> il);
— этоC++11
новый конструктор, использующий списки инициализаторов. См. en.cppreference.com/w/cpp/container/map/insert а> - person vsoftco   schedule 29.07.2014