Могут ли члены std :: forward_list быть реализованы как статические? - PullRequest
9 голосов
/ 19 октября 2011

std::forward_list предоставляет insert_after и erase_after членов, которые, возможно, не нуждаются в фактическом доступе к объекту std::forward_list.Следовательно, они могут быть реализованы как static функции-члены и вызываться без объекта списка - полезно для объекта, который хочет удалить себя из списка, что является очень распространенным использованием. РЕДАКТИРОВАТЬ : Эта оптимизация применяется только к forward_list специализациям на std::allocator или пользовательских распределителях без сохранения состояния.

Может ли реализация, соответствующая стандартам, сделать это?

§17.6.5.5/3:

Вызов подписи функции-члена, описанной в стандартной библиотеке C ++, ведет себя так, как будто реализация не объявляет никаких дополнительных сигнатур функций-членов.

ссноска

Действительная программа на C ++ всегда вызывает ожидаемую функцию-член библиотеки или функцию с эквивалентным поведением.Реализация также может определять дополнительные функции-члены, которые иначе не были бы вызваны действительной программой на C ++.

Мне не ясно, создаст ли добавление static "другую" функцию-член, но удаляю(неявный) аргумент не должен нарушать ничего, что не добавлял бы аргументы по умолчанию, и это законно.(Вы не можете легально использовать PTMF для любой стандартной функции-члена.)

Мне кажется, что библиотеке следует разрешить это делать, но я не уверен, что какое-то правило будет нарушено.И насколько нормативными являются перечисленные прототипы функций-членов?

1 Ответ

9 голосов
/ 19 октября 2011

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

Опасность пользовательских распределителей уже указана. Но даже для std::allocator<T> существует опасность, что кто-то может специализироваться std::allocator<MyType> и затем обнаружить, что allocator::construct/destroy не вызывается.

Хорошо, но можно специализироваться, скажем std::forward_list<int> (нет пользовательского распределителя, нет определенного пользователем значения_типа) и сделать insert_after статическим?

Нет. Это изменение будет обнаружено с новыми возможностями SFINAE. Вот демо:

#include <memory>
#include <iostream>

template <class T, class A = std::allocator<T>>
class forward_list
{
public:
    typedef T value_type;
    struct const_iterator {};
    struct iterator {};

    iterator insert_after(const_iterator p, const T& x);
};

template <class C>
auto test(C& c, typename C::const_iterator p, const typename C::value_type& x)
    -> decltype(C::insert_after(p, x))
{
    std::cout << "static\n";
    return typename C::iterator();
}

template <class C>
auto test(C& c, typename C::const_iterator p, const typename C::value_type& x)
    -> decltype(c.insert_after(p, x))
{
    std::cout << "not static\n";
    return typename C::iterator();
}

int main()
{
    ::forward_list<int> c;
    test(c, ::forward_list<int>::const_iterator(), 0);
}

Эта программа запускается и печатает:

not static

Но если я сделаю insert_after статичным:

static iterator insert_after(const_iterator p, const T& x);

Тогда я получаю ошибку времени компиляции:

test.cpp:34:5: error: call to 'test' is ambiguous
    test(c, ::forward_list<int>::const_iterator(), 0);
    ^~~~
test.cpp:16:6: note: candidate function [with C = forward_list<int, std::__1::allocator<int> >]
auto test(C& c, typename C::const_iterator p, const typename C::value_type& x)
     ^
test.cpp:24:6: note: candidate function [with C = forward_list<int, std::__1::allocator<int> >]
auto test(C& c, typename C::const_iterator p, const typename C::value_type& x)
     ^
1 error generated.

Обнаружена разница.

Таким образом, невозможно сделать forward_list::insert_after статичным.

Обновление

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

template <class C, class ...Args>
auto test(C& c, typename C::const_iterator p, const typename C::value_type& x, Args...)
    -> decltype(c.insert_after(p, x))
{
    std::cout << "not static\n";
    return typename C::iterator();
}

Теперь тест будет распечатывать «статический» или «не статический» в зависимости от того, статическая функция insert_after или нет.

...