Ложноположительный с is_copy_constructible на векторе <unique_ptr>

Должна ли черта типа обрабатывать такие случаи, как std::vector < std::unique_ptr <int> >, и обнаруживать, что она не копируется конструктивно?

Вот пример в https://ideone.com/gbcRUa (запуск g++ 4.8.1)

#include <type_traits>
#include <vector>
#include <iostream>
#include <memory>

int main()
{
    // This prints 1, implying that it copy constructible, when it clearly not
    std::cout << std::is_copy_constructible< std::vector<std::unique_ptr<int> > >::value << std::endl; 
    return 0;
}

Если это правильное поведение для is_copy_constructible, есть ли способ обнаружить, что конструкция копирования плохо сформирована? Ну, кроме того, что он не скомпилируется.

Ответ 1

Это связано с недостатком конструкции std::vector. std::vector определяет конструкцию копирования, даже если он не сможет скомпилироваться, и полагается, что пользователи std::vector не будут вызывать метод, если он не скомпилируется.

Альтернативный дизайн - SFINAE блокирует вызов метода, если тип, содержащийся в vector, не имеет конструктора копирования. Тем не менее, std::vector был разработан до разработки современных методов SFINAE.

Возможно, он может быть встроен в новую итерацию C++, так как будет очень мало кода, который может сломаться. Нельзя сказать, что ни один код не сломается, потому что у вас может быть код, основанный на том факте, что std::is_copy_constructible< std::vector< no_copy_type > > является std::true_type, или эквивалентные выражения, но это довольно странная зависимость.

Вдобавок к тому, что std::vector старше техник SFINAE, которые могут решить эту проблему, делать это с SFINAE довольно грязно (так как SFINAE - грязная техника). Новая концепция-облегченная версия, предложенная для C++ 1y, может сделать ее более чистой и более заманчивой для включения в новую итерацию языка.

Моя работа вокруг, когда у меня есть контейнер, который должен знать, можно ли безопасно скопировать, сравнить и упорядочить содержащийся объект, состоит в том, чтобы специализироваться для std::vector в классе пользовательских свойств и использовать значение класса пользовательских свойств в Содержащийся тип. Это лоскутное и довольно навязчивое решение.

template<template<typename>class test, typename T>
struct smart_test : test<T> {};
template<template<typename>class test, typename T, typename A>
struct smart_test<test, std::vector<T,A>> : smart_test<T> {};

что дает нам:

template<typename T>
using smart_is_copy_constructible = smart_test< std::is_copy_constructible, T >;

и аналогичные для < и ==. Я могу добавить больше специализаций, когда столкнусь с большим количеством типов контейнеров, которые действительно должны перенаправить свои свойства в свои данные, или я мог бы написать более интересный контейнер-тест SFINAE и выполнить traits, извлечь базовый тип-значение и отправить вопрос в тест. на типе значения.

Но по моему опыту, я в основном в итоге выполняю эти тесты на std::vector.

Обратите внимание, что поскольку к вектору были добавлены правила "участвует в разрешении перегрузки", это говорит о стандартах для тестов "do SFINAE".

Ответ 2

В таблице 49 стандарта С++ 11 перечислены, какие условия должен выполняться классом для is_copy_constructable<T>::value, чтобы быть истинным и, к сожалению, не так много:

is_constructable<T, const T&>::value true

Итак, если std::vector<T> имеет конструктор копирования, он проходит тест.