2013-02-11 3 views
10

Следующий код компилируется нормально с GCC 4.7.2 (MinGW)станд :: unordered_map :: устанавливать проблемы с частным/удаляемого конструктор копирования

#include <unordered_map> 
#include <tuple> 

struct test 
{ 
     test() =default; 
    private: 
     test(test const&) =delete; 
}; 

int main() 
{ 
    std::unordered_map<char, test> map; 

    map.emplace(
     std::piecewise_construct, 
     std::forward_as_tuple('a'), 
     std::forward_as_tuple() 
    ); 
} 

Если изменить конструктор копирования в test от test(test const&) =delete; к test(test const&) =default; однако , ошибка шаблона vomit, кажется, жалуется на const test&, не будучи конвертируемой в test (текст here). Не должны ли работать? Или, если нет, не должны ли они оба дать ошибку?

ответ

11

Если вы посмотрите на блевотину ошибки шаблон тщательнее вы будете видеть этот кусок моркови в ней:

test.exe.cpp:8:3: error: 'constexpr test::test(const test&)' is private 

Это ключ к проблеме.

GCC 4.7.2 не выполняет проверку доступа как часть вычитания аргумента шаблона (как это требовалось C++ 03). Свойство is_convertible реализовано с использованием SFINAE, которое основывается на выводе аргумента шаблона и при разрешении перегрузки выбор частного аргумента аргумента конструктора преуспевает, но проверка доступа не выполняется, потому что выбранный конструктор является закрытым. Это проблема с GCC 4.7, поскольку она не была изменена, чтобы следовать новому правилу C++ 11 в 14.8.2 [temp.educt], который гласит:

-8- Если подстановка приводит к недопустимый тип или выражение, сбой вывода типа. Недопустимый тип или выражение - это те, которые были бы плохо сформированы, если они были написаны с использованием замещенных аргументов. [Примечание: Проверка доступа выполняется как часть процесса замещения. -end примечание]

Это огромное изменение предыдущих правил вывода, ранее этот пункт сказал

-8- Если подмена приводит к недопустимому типу или выражение, типа вычет не может , Недопустимый тип или выражение - это те, которые были бы плохо сформированы, если они были написаны с использованием замещенных аргументов. Проверка доступа не выполняется как часть процесса замещения. Следовательно, когда дедукция завершается успешно, ошибка доступа может возникать при создании экземпляра функции.

Это изменение было сделано достаточно поздно в процессе C++ 0x по DR 1170, и делает SFINAE совершенно удивительным в C++ 11 :)

GCC 4.8 реализует новые правила, так is_convertible и сходные черты дать правильный ответ для недоступных конструкторов.

4

Правильный ответ - Джонатан Уэйли. Я оставлю это, поскольку он предоставляет полезную информацию для людей, у которых есть аналогичная проблема, связанная с insert.


Короткая версия, что это вызвано проблемой в реализации стандартной библиотеки, используемой GCC 4.7.2, в результате чего из вводящей в заблуждение формулировки, используемые в C++ 11 Стандарт на. Существует предложение об изменении формулировки, а также исправление реализации в GCC 4.8.


Длинная версия

This GCC bug entry сообщает очень похожий вопрос, где insert используется вместо emplace. LibstdC++ реализация из insert следует стандарт, в котором говорится о insert функции (в частности, template <class P> pair<iterator,bool> insert(P&& obj)):

(§23.5.4.4/5) Примечания: Эта подпись не будет участвовать в разрешении перегрузки, если P не является неявно конвертируемым до value_type.

libstdC++ кажется, реализовали это требование, используя enable_if заявление, которое проверяет std::is_convertible<> для типов участвующих.

Отчет об ошибке, упомянутый выше, гласит, что действительно необходимо использовать std::is_constructible<>, а формулировку в Стандарте следует изменить. Он связывает с ПРГ (язык рабочей группы) вопросом, в котором предлагается изменение к Стандарту для этого уже (LWG issue #2005, увидеть Portland 2012 записи, соответствующую часть предлагаемого изменения ниже):

  1. Изменение 23.5.4.4 [unord.map.modifers] на стр. 1, как показано:

    template <class P> 
    pair<iterator, bool> insert(P&& obj); 
    

[...] Примечание: Эта подпись не должна участвовать в разрешении перегрузки, если P не неявно конвертируются в VALUE_TYPE std::is_constructible<value_type, P&&>::value верно.

Предлагаемое изменение также утверждает, что действие функции insert описанного выше должно быть эквивалентна emplace(std::forward<P>(obj)). Поэтому, вероятно, можно с уверенностью сказать, что проблема, описанная в вашем вопросе, является точно такой же проблемой.

И действительно, предложенные изменения, как представляются, отражен в последних GCC 4.8 снимок: При компиляции коды с GCC 4.8, то is_convertible проверка не выполняется и не появляется сообщений об ошибке.

+1

Закрыть, но нет сигары :) LWG 2005 применяется только к 'insert' not' emplace', причина, по которой программа выходит из строя с GCC 4.7.2, заключается в том, что она не выполняет проверку доступа как часть вывода аргумента шаблона (as требуется в C++ 03), поэтому 'is_constructible' получает отказ доступа из-за частного конструктора. GCC 4.8 реализует правила C++ 11 и проверяет доступ во время вывода аргумента шаблона. –

+2

. Вы можете подтвердить разницу не из-за каких-либо изменений в стандартной библиотеке путем предварительной обработки кода с помощью G ++ 4.7 (поэтому он использует библиотеку из 4.7), а затем компилирует это с 4.8, и в этом случае программа работает, доказывая, что это был компилятор, а не библиотека, которая изменилась –