C++ 什么';有自定义分配器但没有其他参数的std::function构造函数的要点是什么?

C++ 什么';有自定义分配器但没有其他参数的std::function构造函数的要点是什么?,c++,c++11,std-function,C++,C++11,Std Function,我正在使用std::function和自定义分配器,但当我没有为函数提供初始functor时,它的行为与我预期的不一样 当我向构造函数提供自定义分配器但没有初始函子时,分配器从未被使用过,或者看起来是这样 这是我的密码 //Simple functor class that is big to force allocations struct Functor128 { Functor128() {} char someBytes[128]; void oper

我正在使用std::function和自定义分配器,但当我没有为函数提供初始functor时,它的行为与我预期的不一样

当我向构造函数提供自定义分配器但没有初始函子时,分配器从未被使用过,或者看起来是这样

这是我的密码

//Simple functor class that is big to force allocations
struct Functor128
{
    Functor128()
    {}

    char someBytes[128];

    void operator()(int something)
    {
        cout << "Functor128 Called with value " << something << endl;
    }
};

int main(int argc, char* argv[])
{
Allocator<char, 1> myAllocator1;
Allocator<char, 2> myAllocator2;
Allocator<char, 3> myAllocator3;
Functor128 myFunctor;

cout << "setting up function1" << endl;
function<void(int)> myFunction1(allocator_arg, myAllocator1, myFunctor);
myFunction1(7);

cout << "setting up function2" << endl;
function<void(int)> myFunction2(allocator_arg, myAllocator2);
myFunction2 = myFunctor;
myFunction2(9);

cout << "setting up function3" << endl;
function<void(int)> myFunction3(allocator_arg, myAllocator3);
myFunction3 = myFunction1;
myFunction3(19);
}
所以案例1:myFunction1按预期使用分配器1进行分配

案例2:myFunction2在构造函数中被赋予了分配器2,但当被赋予一个functor时,它似乎会重置为使用默认的std::allocator进行分配。(因此没有关于分配的打印输出)

案例3:myFunction3在构造函数中被分配给分配器3,但当从myFunction1分配给时,使用function1的分配器进行分配

这是正确的行为吗? 特别是,在案例2中,为什么要恢复使用默认的std::allocator? 如果是这样,那么将分配器作为永远不会被使用的分配器的空构造函数有什么意义呢

我将VS2013用于此代码

我的分配器类只是一个最小的实现,它使用new并在分配时注销

template<typename T, int id = 1>
class Allocator {
public:
    //    typedefs
    typedef T value_type;
    typedef value_type* pointer;
    typedef const value_type* const_pointer;
    typedef value_type& reference;
    typedef const value_type& const_reference;
    typedef std::size_t size_type;
    typedef std::ptrdiff_t difference_type;

public:
    //    convert an allocator<T> to allocator<U>
    template<typename U>
    struct rebind {
        typedef Allocator<U> other;
    };

public:
    inline  Allocator() {}
    inline ~Allocator() {}
    inline  Allocator(Allocator const&) {}
    template<typename U>
    inline  Allocator(Allocator<U> const&) {}

    //    address
    inline pointer address(reference r) { return &r; }
    inline const_pointer address(const_reference r) { return &r; }

    //    memory allocation
    inline pointer allocate(size_type cnt,
        typename std::allocator<void>::const_pointer = 0) 
    {
        size_t numBytes = cnt * sizeof (T);
        std::cout << "Allocator " << id <<  " allocating " << numBytes << " bytes." << std::endl;
        return reinterpret_cast<pointer>(::operator new(numBytes));
    }
    inline void deallocate(pointer p, size_type) {
        ::operator delete(p);
    }

    //    size
    inline size_type max_size() const {
        return std::numeric_limits<size_type>::max() / sizeof(T);
    }

    //    construction/destruction
    inline void construct(pointer p, const T& t) { new(p)T(t); }
    inline void destroy(pointer p) { p->~T(); }

    inline bool operator==(Allocator const&) { return true; }
    inline bool operator!=(Allocator const& a) { return !operator==(a); }
};    //    end of class Allocator
模板
类分配器{
公众:
//typedefs
类型定义T值_类型;
typedef值\u type*指针;
类型定义常量值\u类型*常量指针;
类型定义值\u类型和参考;
类型定义常量值\u类型和常量参考;
typedef std::size\u t size\u type;
typedef std::ptrdiff_t difference_type;
公众:
//将分配器转换为分配器
模板
结构重新绑定{
类型定义分配器其他;
};
公众:
内联分配器(){}
内联~Allocator(){}
内联分配器(分配器常数&){}
模板
内联分配器(分配器常数&){}
//地址
内联指针地址(引用r){return&r;}
内联常量指针地址(常量引用r){return&r;}
//内存分配
内联指针分配(大小\类型cnt,
typename std::分配器::常量指针=0)
{
size_t numBytes=cnt*sizeof(t);

std::cout
std::function
的分配器支持非常奇怪

operator=(F&&F)
的当前规范是它执行
std::function(std::forward(F)).swap(*this)
。正如您所见,这意味着
f
的内存是使用默认情况下
std::function
使用的任何函数分配的,而不是使用分配器来构造
*此
。因此您观察到的行为是正确的,尽管令人惊讶

此外,由于
(allocator\u arg\u t,allocator)
(allocator\u arg\u t,allocator,nullptr\u t)
构造函数是
noexcept
,即使它们想存储分配器,它们也不能真正存储分配器(删除分配器可能需要动态分配)。实际上,它们基本上没有支持uses分配器构造协议的ops


LWG最近拒绝了改变这种行为的方案。

他们口头上支持uses分配器构造协议(和
std::scoped_分配器_适配器),但实际上他们并不支持它。也许这就是GCC选择不实施口头服务的原因。我提出了另一种解决方案:(PDF)。是否规定保存在
std::function
中的可调用对象应由
std::function
使用的分配器删除?还是仅使用
delete p
?@linux40分配器在类型擦除后不能对任意类型执行
构造
销毁
(但请参见,其中谈到了
构造
,但同样适用于
销毁
)。解除分配显然必须使用分配器完成;析构函数很可能直接调用。但是,正如您所说,
std::function
不能真正存储分配器,有没有办法使用此分配器?在我看来,
std::function
可以存储分配器,如果它使用虚拟类实现,那么e> 也可以使用construct
destroy
。@linux40哦,你是说这两个特定的重载?它们只是乱七八糟。请注意,它们无论如何都不能取消分配,因为分配目标将使用不同的分配机制。有人建议从
std::function
中删除分配器支持:使用已知问题的有用列表
template<typename T, int id = 1>
class Allocator {
public:
    //    typedefs
    typedef T value_type;
    typedef value_type* pointer;
    typedef const value_type* const_pointer;
    typedef value_type& reference;
    typedef const value_type& const_reference;
    typedef std::size_t size_type;
    typedef std::ptrdiff_t difference_type;

public:
    //    convert an allocator<T> to allocator<U>
    template<typename U>
    struct rebind {
        typedef Allocator<U> other;
    };

public:
    inline  Allocator() {}
    inline ~Allocator() {}
    inline  Allocator(Allocator const&) {}
    template<typename U>
    inline  Allocator(Allocator<U> const&) {}

    //    address
    inline pointer address(reference r) { return &r; }
    inline const_pointer address(const_reference r) { return &r; }

    //    memory allocation
    inline pointer allocate(size_type cnt,
        typename std::allocator<void>::const_pointer = 0) 
    {
        size_t numBytes = cnt * sizeof (T);
        std::cout << "Allocator " << id <<  " allocating " << numBytes << " bytes." << std::endl;
        return reinterpret_cast<pointer>(::operator new(numBytes));
    }
    inline void deallocate(pointer p, size_type) {
        ::operator delete(p);
    }

    //    size
    inline size_type max_size() const {
        return std::numeric_limits<size_type>::max() / sizeof(T);
    }

    //    construction/destruction
    inline void construct(pointer p, const T& t) { new(p)T(t); }
    inline void destroy(pointer p) { p->~T(); }

    inline bool operator==(Allocator const&) { return true; }
    inline bool operator!=(Allocator const& a) { return !operator==(a); }
};    //    end of class Allocator