Вы можете ввести преднамеренную ошибку неоднозначности, когда присутствуют как конструктор перемещения, так и конструктор копирования. Это позволяет нам проверять наличие конструктора перемещения.
В последние годы, когда меняются компиляторы, разные решения работают, а затем ломаются. Это работает с Clang 3.5.0. Я надеюсь, что он будет работать и на старых, и на новых компиляторах, но я не специалист по стандарту.
Кроме того, этот ответ требует больше работы, чтобы закончить его, но я проверил основную идею.
Во-первых, легко определить, существует ли конструктор копирования. Если конструктора копирования нет, тогда легко определить, существует ли конструктор перемещения. Задача состоит в том, чтобы, когда является конструктором копирования, проверить, существует ли также конструктор перемещения. Это проблема, на которой я остановлюсь.
Поэтому достаточно рассмотреть только те типы, у которых есть конструктор копирования, и проверить наличие конструктора перемещения. В оставшейся части этого вопроса я предполагаю, что присутствует конструктор копирования.
Я проверяю конструктор перемещения, выдавая ошибку неоднозначности, когда присутствуют оба вида конструктора, и затем (ab) использую SFINAE для проверки на наличие этой неоднозначности.
Другими словами, наша задача - проверить разницу между следующими типами:
struct CopyOnly {
CopyOnly (const CopyOnly&); // just has a copy constructor
};
struct Both {
Both (const Both&); // has both kinds of constructor
Both (Both&&);
};
Для этого сначала определите класс Converter<T>
, который утверждает, что может преобразовывать себя в два вида ссылок. (Нам никогда не понадобится это реализовывать)
template<typename T>
struct Converter {
operator T&& ();
operator const T& ();
};
Во-вторых, рассмотрим эти строки:
Converter<T> z;
T t(z);
Вторая строка пытается построить T
. Если T
равно CopyOnly
, то через конструктор копирования будет сделано t
, а соответствующая ссылка для передачи в конструктор копирования извлечена из метода operator const CopyOnly &()
из Converter<CopyOnly>
. Пока что это довольно стандартно. (Я думаю?)
Но, если T
равно Both
, т. Е. У него также есть конструктор перемещения, возникнет ошибка неоднозначности. Доступны оба конструктора T
, поскольку для обоих доступны конвертеры (конвертеры от z
), поэтому существует неоднозначность. (Любые языковые юристы могут подтвердить, что это полностью стандарт?)
Эта логика применима также к new T( Converter<T>{} )
. Это выражение имеет тип тогда и только тогда, когда T
не имеет конструктора перемещения. Поэтому мы можем обернуть decltype
вокруг этого и использовать это в SFINAE.
Я закрываюсь с двумя перегрузками baz<T>
. Выбранная перегрузка будет зависеть от того, будет ли T
похож на CopyOnly
или Both
. Первая перегрузка действительна только в том случае, если new T( Converter<T>{} )
четко определено, т. Е. Если нет ошибки неоднозначности, т. Е. Если нет конструктора перемещения. Вы можете указать разные типы возврата для каждой перегрузки, чтобы сделать эту информацию доступной во время компиляции.
template<typename T>
std:: true_type
baz (decltype( new T( Converter<T>{} ) )) {
cout << __LINE__ << endl;
return {};
}
template<typename U>
std:: false_type
baz (
const volatile // const volatile to tie break when both forms of baz are available
U *) {
cout << __LINE__ << endl;
return {};
}
baz
следует называть так:
baz<JustCopy>((JustCopy*)nullptr);
baz<Both>((Both*)nullptr);
И вы могли бы обернуть это во что-то вроде этого:
template<typename T>
struct has_move_constructor_alongside_copy {
typedef decltype(baz<T>((T*)nullptr)) type;
};
Есть много дел, чтобы привести это в порядок, и я уверен, что эксперты SFINAE могли бы значительно его улучшить (пожалуйста!). Но я думаю, что это решает основную проблему, проверяя наличие конструктора перемещения, когда мы уже знаем, присутствует ли конструктор копирования.