[FYI] Fail: попытка написать is_swappable на скорую руку
От: zaufi Земля  
Дата: 02.11.14 17:48
Оценка:
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 и шаблонными функциями!
Отредактировано 03.11.2014 3:14 zaufi . Предыдущая версия . Еще …
Отредактировано 02.11.2014 17:49 zaufi . Предыдущая версия .
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.