Сегодня я обнаружил, что не понимаю правила приоритета конструктора C++.
Пожалуйста, смотрите следующий шаблон struct wrapper
template <typename T>
struct wrapper
{
T value;
wrapper (T const & v0) : value{v0}
{ std::cout << "value copy constructor" << std::endl; }
wrapper (T && v0) : value{std::move(v0)}
{ std::cout << "value move constructor" << std::endl; }
template <typename ... As>
wrapper (As && ... as) : value(std::forward<As>(as)...)
{ std::cout << "emplace constructor" << std::endl; }
wrapper (wrapper const & w0) : value{w0.value}
{ std::cout << "copy constructor" << std::endl; }
wrapper (wrapper && w0) : value{std::move(w0.value)}
{ std::cout << "move constructor" << std::endl; }
};
Это простая оболочка значения шаблона с конструктором копирования (wrapper const &
), конструктором перемещения (wrapper && w0
), своего рода конструктором копирования значений (T const & v0
), своего рода конструктором перемещения (T && v0
) и своего рода шаблонной конструкцией на месте. -конструктор значений (As && ... as
, по примеру emplace
методов для контейнеров STL).
Мое намерение состояло в том, чтобы использовать вызов конструктора копирования или перемещения с оболочкой, конструктор копирования или перемещения значения, передающий объект T
, и вызов конструктора шаблона emplace со списком значений, способных создать объект типа T
.
Но я не получаю того, что ожидал.
Из следующего кода
std::string s0 {"a"};
wrapper<std::string> w0{s0}; // emplace constructor (?)
wrapper<std::string> w1{std::move(s0)}; // value move constructor
wrapper<std::string> w2{1u, 'b'}; // emplace constructor
//wrapper<std::string> w3{w0}; // compilation error (?)
wrapper<std::string> w4{std::move(w0)}; // move constructor
Значения w1
, w2
и w4
создаются с помощью конструктора перемещения значения, конструктора emplace и конструктора перемещения (соответственно), как и ожидалось.
Но w0
создается с помощью конструктора emplace (я ожидал конструктора копирования значений), а w3
вообще не создается (ошибка компиляции), потому что конструктор emplace предпочтительнее, но не является конструктором std::string
, который принимает значение wrapper<std::string>
.
Первый вопрос: что я делаю не так?
Я предполагаю, что проблема w0
связана с тем, что s0
не является значением const
, поэтому T const &
не является точным совпадением.
Действительно, если я напишу
std::string const s1 {"a"};
wrapper<std::string> w0{s1};
Я получаю конструктор копии значения, называемый
Второй вопрос: что я должен сделать, чтобы получить то, что я хочу?
Итак, что мне нужно сделать, чтобы конструктор копирования значений (T const &
) имел приоритет над конструктором emplace (As && ...
) также с неконстантными значениями T
и, в основном, что мне нужно сделать, чтобы конструктор копирования (wrapper const &
) взял приоритет построения w3
?
T&
иwrapper&
, а неT const&
иwrapper const&
. Таким образом, идеальный конструктор переадресации находится первым. Возможные решения включают отключение идеального конструктора пересылки, если другие конструкторы совпадают (через SFINAE), или, возможно, добавление перегрузкиT&
иwrapper&
(я не видел, чтобы это решение часто использовалось, но оно должно работать) - person Justin   schedule 20.08.2018in_place_t
(украденный уstd::variant
) или любой другой тег по вашему выбору. - person SergeyA   schedule 20.08.2018wrapper (const As && ... as)
, она больше не будет вызываться в пользу вашего ctor значения. - person cplusplusrat   schedule 20.08.2018T&
(при передачеstd::string const
вызывается конструкторT const &
), но (я не понимаю почему) дляwrapper &
. - person max66   schedule 20.08.2018in_place_t
предложение как ответ; это не кажется мне действительно удовлетворительным, но это лучше, чем ничего. - person max66   schedule 20.08.2018const
несовместим с идеальной пересылкой; во всяком случае, ваше наблюдение, кажется, подтверждает, что это проблема константности - person max66   schedule 20.08.2018wrapper &
иwrapper const &
: сделатьw0
константой,w3
скомпилировать с конструктором копирования; этоw4
выдает ошибку (надо еще поспать!). - person max66   schedule 20.08.2018