hi all,
пребывая в эйфории от decltype и простоты написания нынче всяких type_traitsов, я наткнулся проблемку. началось все с того, что мне нужно было определить можно ли делать swap() для некого типа T. для обмена значениями может быть использован как std::swap, так и свободная функция выводимая через ADL (см. 17.6.3.2).
с определением возможности вызывать swap по ADL проблем не возникло. "магия" началась при попытке использовать такой же подход для определения возможности вызывать std::swap. в конечном итоге я сделал минимальный пример демонстрирующий "проблему":
#include <iostream>
#include <utility>
struct not_swappable
{
/// Delete copy ctor
not_swappable(const not_swappable&) = delete;
/// Delete copy-assign operator
not_swappable& operator=(const not_swappable&) = delete;
/// Delete move ctor
not_swappable(not_swappable&&) = delete;
/// Delete move-assign operator
not_swappable& operator=(not_swappable&&) = delete;
};
template <typename T>
using swappable_t = typename std::add_pointer<
decltype(
std::swap(
std::declval<T&>()
, std::declval<T&>()
)
)
>::type;
template <typename T>
typename std::enable_if<
std::is_same<void*, swappable_t<T>>::value
>::type test()
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
int main()
{
#if 0
// It doesn't compile (as expected)
not_swappable a;
not_swappable b;
std::swap(a, b);
#endif
// WTF! Why it compiles w/o error!?
test<not_swappable>();
return 0;
}
собственно "проблема" (я пока не нашел полного подтверждения того, что это действительно проблема, равно как и того, что так и должно быть) обозначена комментом "WTF!".
и мое текущее понимание этой проблемы состоит в том, что:
0. std::swap это шаблонная функция
1. первый закомменченный кусок, разумеется, не компилится, т.к. мы явно запретили copy/move ctor/assign -- т.е. ни какой возможности копировать/переносить объекты not_swappable у функции std::swap нету!
2. единственное объяснение того факта, что код под WTF компилится, это что decltype(), разумеется, нашел шаблонную функцию swap, подставил туда non_swappable, и на этом "успокоился"...
и вправду, подстановка T в шаблон swap никак не ломает сигнатуру ... стандарт ничего не говорит о том, что swap должен использовать std::enable_if и проверять тип T на is_move_constructible и is_move_assignable, поэтому сигнатура функции не "пострадала" и SFINAE ничего "не заметил"... а при попытке инстанциировать swap (в первом закомменченом куске) мы, разумеется, обломались в теле функции и std::swap была выкинута из списка кандидатов.
мой предидущий опытАвтор: zaufi
Дата: 23.07.13
подсказываетАвтор: zaufi
Дата: 24.07.13
, что decltype() это
unevaluated context -- т.е. туловище std::swap даже не пытаемся анализировать... но тысяча чертей! как же тогда быть? если decltype норовит обмануть!??
ну фиг с ним с std::swap -- выкрутился тем, что сам проверяю что T должен быть is_move_constructible и is_move_assignable -- значит std::swap сработает, но если представить что-то более навороченное... ну не знаю, допустим какой-нить шаблонный алгоритм, требующий определенных свойств у итератора и элементов... этож получается, что decltype будет показывать всегда, что функцию можно вызвать... хотя по факту нет... и нужно смотреть в реализацию (возможно из другой/чужой библы, а она может меняться!), и собирать все эти требуемые свойства в кучу... писать сложные type_traits, складывать их по && & etc... запарно как-то...
в общем, FYI, что называется...
будьте осторожны коллеги с decltype и шаблонными функциями!