可以std :: forward_list成员实现为静态? [英] Can std::forward_list members be implemented as static?

查看:165
本文介绍了可以std :: forward_list成员实现为静态?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

std :: forward_list 提供 insert_after erase_after 成员可能不需要实际访问 std :: forward_list 对象。因此,它们可以作为 static 成员函数实现,并且可以在没有列表对象的情况下调用 - 对于想从列表中删除自身的对象非常有用,这是非常常见的用法。 EDIT :此优化仅适用于 std :: allocator 上的 forward_list



§17.6.5.5/ 3说明这是一个符合标准的实现。


调用C ++标准库中描述的成员函数签名的行为好像实现
声明没有其他成员函数签名。


带有脚注


有效的C ++程序总是调用预期的库成员函数,或具有等效行为的函数。一个实现也可以定义另外的成员函数,否则不能被有效的C ++程序调用。


static 会创建一个不同的成员函数,但删除一个(隐式)参数不应该破坏任何添加默认参数不会,并且是合法的。 (你不能合法地将PTMF带到任何标准的成员函数。)



它让我想到图书馆应该允许这样做,但我不知道规则会被打破。

解决方案

标准说如果没有人可以告诉区别。你是正确的,不能合法地创建一个PTMF到 forward_list ,所以你这么安全。



已经指出了自定义分配器的危险。但是即使对于 std :: allocator< T> 也有一种危险,有人可以专门化 std :: allocator< MyType> 然后检测到 allocator :: construct / destroy 没有被调用。



好的,一个专门说 std :: forward_list< int> (没有自定义分配器,没有用户定义的value_type),并使 insert_after static ?



否。这种变化可以用新的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 {};

迭代器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\\\
;
return typename C :: iterator();
}

模板< 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\\\
;
return typename C :: iterator();
}

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

此程序运行并输出:

 不是静态的

insert_after static:

  static iterator insert_after(const_iterator p,const T& X);然后我得到一个编译时错误:


test.cpp:34:5:错误:调用'test'是不明确的
test(c,:: forward_list< int> :: const_iterator ;
^ ~~~
test.cpp:16:6:note:候选函数[with C = forward_list auto test(C& c,typename C :: const_iterator p,const typename C :: value_type& x)
^
test.cpp:24:6:note函数[with C = forward_list< int,std :: __ 1 :: allocator< int> >]
自动测试(C& c,typename C :: const_iterator p,const typename C :: value_type& x)
^
产生1个错误。

检测到差异。


$ b b

因此,不符合 forward_list :: insert_after static。



更新



如果你想让静态重载可调用,你只需要使它比不静态重载更合适。一种方法是将非静态重载更改为:

 模板< 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\\\
;
return typename C :: iterator();
}



现在测试将打印出静态或不静态关于 insert_after 成员函数是否是静态的。


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?

解决方案

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.

这篇关于可以std :: forward_list成员实现为静态?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆