Утверждать, что код не компилируется

короче:

Как написать тест, который проверяет, что мой класс не копируется или не копируется, а только перемещается и перемещается?

В общем:

Как написать тест, который гарантирует, что конкретный код не компиляции? Вот так:

// Movable, but non-copyable class
struct A
{
  A(const A&) = delete;
  A(A&&) {}
};

void DoCopy()
{
  A a1;
  A a2 = a1;
}

void DoMove()
{
  A a1;
  A a2 = std::move(a1);
}

void main()
{
  // How to define these checks?
  if (COMPILES(DoMove)) std::cout << "Passed" << std::endl;
  if (DOES_NOT_COMPILE(DoCopy)) std::cout << "Passed" << std::endl;
}

Я думаю, что-то связано с SFINAE, но есть ли готовые решения, возможно, в boost?

5 ответов


template<class T>struct sink{typedef void type;};
template<class T>using sink_t=typename sink<T>::type;

template<typename T, typename=void>struct my_test:std::false_type{};
template<typename T>struct my_test<T,
  sink_t<decltype(

поместите здесь код. Обратите внимание, что он должен "не рано", т. е. в сигнатуре функции, а не в теле

  )>
>:std::true_type {};

выше генерирует тест, если "положить код здесь" можно оценить.

чтобы определить, нельзя ли оценить "поместить код сюда", отрицайте результат теста.

template<class T>using not_t=std::integral_constant<bool, !T::value>;
not_t< my_test< int > >::value

будет true iff "put code here" терпит неудачу на этапе подстановки. (или вы можете сделать это вручную, путем замены std::true_type и std::false_type выше.)

сбой на этапе замены отличается от общего сбоя, и поскольку это должно быть выражение, вы несколько ограничены в том, что вы можете сделать. Однако, чтобы проверить, возможно ли копирование, вы можете сделать:

template<typename T, typename=void>struct copy_allowed:std::false_type{};
template<typename T>struct copy_allowed<T,
  sink_t<decltype(
    T( std::declval<T const&>() )
  )>
>:std::false_type {};

и двигаться:

template<typename T, typename=void>struct move_allowed:std::false_type{};
template<typename T>struct move_allowed<T,
  sink_t<decltype(
    T( std::declval<T>() )
  )>
>:std::false_type {};

и двигаться только:

template<typename T>struct only_move_allowed:
  std::integral_constant<bool, move_allowed<T>::value && !copy_allowed<T>::value >
{};

общая техника выше полагается на SFINAE. Базовый класс признаков выглядит так:

template<class T, typename=void> struct whatever:std::false_type{};

здесь типа T, а второй (анонимный) параметр по умолчанию void. В библиотеке промышленной силы мы спрятали бы это как деталь реализации (общественная черта перешла бы к такой частной черте.

затем мы специализируемся.

template<typename T>struct whatever<T, /*some type expression*/>:std::true_type{};

фишка в том, что мы делаем /*some type expression*/ оценить по типу void если и только если мы хотим, чтобы наш тест был пройден. Если это не удастся, мы можем либо оценить не -void type, или просто имеют сбой замены происходят.

если и только если он оценивает void мы true_type.

на sink_t< некоторые выражения типа> метод любой введите выражение и превратит его в void: в основном это тест на отказ замены. sink в теории графов относится к месту, где вещи текут в, и ничего не выходит из -- в этом случае,void ничего, и тип перетекает в него.

выражения типа, мы используем decltype( некоторое выражение Не-типа ), что позволяет нам оценивать его в "поддельном" контексте, где мы просто выбрасываем результат. Выражение non-type теперь оценивается только для целей SFINAE.

Примечание обеспечивает MSVC 2013 ограничено или отсутствует поддержка данного шага. Они называют это "выражение SFINAE". Необходимо использовать альтернативные методы.

выражение, отличное от типа, получает свой тип. Это на самом деле не работает, и это не вызывает использования ODR ничего. Так что мы можем использовать std::declval<X>() для создания "поддельных" экземпляров типа X. Мы используем X& для lvalues,X для rvalues, и X const& на const значения lvalue.


вы ищете черты типа, определена в <type_traits>, чтобы проверить, имеют ли типы определенные свойства.


Если цель состоит в том, чтобы гарантировать, что код не будет компилироваться, вы не можете имейте это как часть вашей тестовой программы, так как в противном случае ваш тест программа не компилируется. Вы должны вызвать компилятор на нем, и посмотрите, что такое код возврата.


хороший ответ дается в конце большой статьи"диагностируемых действия" Анджея Krzemieński:

практический способ проверить, не компилируется ли данная конструкция, - это сделать это извне C++: подготовить небольшую тестовую программу с ошибочной конструкцией, скомпилировать ее и проверить, сообщает ли компилятор о сбое компиляции. Вот как" отрицательные " модульные тесты работают с Boost.Строить. Например, см. эту отрицательную тестовую форму Boost.Факультативный библиотека: optional_test_fail_convert_from_null.СРР. В файле конфигурации он аннотируется как compile-fail, что означает, что тест проходит только в случае сбоя компиляции.


возможно, вам придется структурировать свой код немного по-другому, чтобы использовать его, но похоже, что вы ищете

static_assert ( bool_constexpr , сообщений )

выполняется во время компиляции утверждение проверка (начиная с C++11): объяснение: bool_constexpr - постоянное выражение, которое контекстуально конвертируемых в bool; message - строковый литерал, который будет появляются как ошибка компилятора если значение bool_constexpr равно false. Ля статическое объявление assert может отображаться в области блока (как блок объявление) и внутри тела класса (как объявление члена)