Возможная регрессия в g++ 6.1.0
Следующий код
#include <string>
#include <map>
#include <cassert>
struct AV {
explicit AV(std::string const&) {}
};
#if 1
static void check_cache_item(
std::map<std::string, std::string> const& items) // FIXME remove
{
assert(!items.empty());
}
#endif
static void check_cache_item(
std::map<std::string, AV> const& items)
{
assert(!items.empty());
}
int main()
{
check_cache_item({ { "id", "0" }, { "pk", "#0" } });
check_cache_item({ { "id", "0" }, { "pk", "#1" } });
check_cache_item({ { "id", AV{"0"} }, { "pk", AV{"#1"} } });
}
принимается g++ 4.8.4, g++ 5.3.0, clang++ 3.9.0; но g++ 6.1.0 дает ошибку:
cci.cc: In function ‘int main()’:
cci.cc:25:55: error: call of overloaded ‘check_cache_item(<brace-enclosed initializer list>)’ is ambiguous
check_cache_item({ { "id", "0" }, { "pk", "#0" } });
^
cci.cc:10:17: note: candidate: void check_cache_item(const std::map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >&)
static void check_cache_item(
^~~~~~~~~~~~~~~~
cci.cc:16:17: note: candidate: void check_cache_item(const std::map<std::__cxx11::basic_string<char>, AV>&)
static void check_cache_item(
^~~~~~~~~~~~~~~~
cci.cc:26:55: error: call of overloaded ‘check_cache_item(<brace-enclosed initializer list>)’ is ambiguous
check_cache_item({ { "id", "0" }, { "pk", "#1" } });
^
cci.cc:10:17: note: candidate: void check_cache_item(const std::map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >&)
static void check_cache_item(
^~~~~~~~~~~~~~~~
cci.cc:16:17: note: candidate: void check_cache_item(const std::map<std::__cxx11::basic_string<char>, AV>&)
static void check_cache_item(
^~~~~~~~~~~~~~~~
cci.cc: At global scope:
cci.cc:10:17: warning: ‘void check_cache_item(const std::map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >&)’ defined but not used [-Wunused-function]
static void check_cache_item(
^~~~~~~~~~~~~~~~
Если я #ifdef из первого конструктора, то каждый компилятор выдает ошибку (правильно, потому что конструктор AV явный).
Является ли это регрессией в g++ 6.1.0?
Ответы
Ответ 1
Это удивительный и несколько неудачный аспект Стандарта (я бы зашел так далеко, чтобы назвать его дефектом); это результат столкновения правил разрешения перегрузки для инициализации списка копий ([over.match.list], как подтверждено в CWG 1228) и конструктор пересылки элементов pair
(согласно n4387).
gcc ( >= 6.1.0) правильно отклонить вашу программу; clang неверно принять его. Более ранние версии gcc принимают вашу программу, потому что они еще не реализовали n4387; clang принимает вашу программу, потому что исключает явные конструкторы из соображений для разрешения перегрузки для инициализации списка копий, что нарушает [over.match.list] в соответствии со стандартом (Вызов явного конструктора со списком с фиксированным набором: неоднозначным или нет?)
Если мы устраняем посторонние аспекты вашей программы, это сводится к простому вопросу о разрешении перегрузки:
struct A { explicit A(int, int); };
struct B { B(int, int); };
void f(A);
void f(B);
int main() { f({0, 0}); }
Здесь A
стоит за pair<std::string const, AV>
, а B
стоит за pair<string const, string>
. Конструктор A
является явным, поскольку шаг n4387 включает явный конструктор AV
; но за CWG 1228 правила для инициализации списка копий:
[...] включают все конструкторы, но указывают, что программа плохо сформирована, если явный конструктор выбран с помощью разрешения перегрузки. [...]
[over.match.list]:
[...] В инициализации списка копий, если выбран конструктор explicit
, инициализация плохо сформирована. [Примечание. Это отличается от других ситуаций ([over.match.ctor], [over.match.copy]), где рассматриваются только конструкторы преобразования для инициализации копии. Это ограничение применяется только в том случае, если эта инициализация является частью окончательного результата разрешения перегрузки. - конечная нота]
Таким образом, ваша программа правильно рассмотрена (по стандарту, как она есть в настоящее время), чтобы быть неоднозначной.
Дальнейшее чтение: Что может пойти не так, если инициализация списка экземпляров разрешена явным конструкторам?