Can std::forward_list members be implemented as static?

人走茶凉 提交于 2019-12-10 12:43:03

问题


std::forward_list provides insert_after and erase_after members which may not need to actually access the std::forward_list object. Therefore they can be implemented as static member functions and be called without a list object — useful for an object that wants to delete itself from a list, which is a very common use. EDIT: This optimization only applies to forward_list specializations on std::allocator or user-defined stateless allocators.

Can a standard-conforming implementation do this?

§17.6.5.5/3 says

A call to a member function signature described in the C++ standard library behaves as if the implementation declares no additional member function signatures.

with a footnote

A valid C++ program always calls the expected library member function, or one with equivalent behavior. An implementation may also define additional member functions that would otherwise not be called by a valid C++ program.

It's not clear to me whether adding static would create a "different" member function, but removing an (implicit) argument shouldn't break anything that adding defaulted arguments wouldn't, and that is legal. (You cannot legally take a PTMF to any standard member function.)

It strikes me that the library should be allowed to do this, but I'm not sure if some rule would be broken. And how normative are the listed member function prototypes?


回答1:


The standard says you can get away with it if no one can tell the difference. And you are correct that one can not legally create a PTMF into forward_list, so you're safe that way.

The danger of custom allocators has already been pointed out. But even for std::allocator<T> there is a danger that someone could specialize std::allocator<MyType> and then detect that the allocator::construct/destroy wasn't being called.

Okay, but can one specialize say std::forward_list<int> (no custom allocator, no user defined value_type) and make insert_after static?

No. This change would be detectable with the new SFINAE capabilities. Here is a demo:

#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);
}

This program runs and prints out:

not static

But if I make insert_after static:

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

Then I get a compile time error:

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.

Difference detected.

Thus it is non-conforming to make forward_list::insert_after static.

Update

If you want to make the "static" overload callable, you simply need to make it slightly more desirable than the "not static" overload. One way of doing that is changing the "not static" overload to:

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();
}

Now the test will print out either "static" or "not static" depending on whether the insert_after member function is static or not.



来源:https://stackoverflow.com/questions/7820981/can-stdforward-list-members-be-implemented-as-static

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!