为什么bind1st和bind2nd需要常量函数对象?

发布于 2024-08-31 19:39:41 字数 2454 浏览 2 评论 0原文

所以,我正在编写一个 C++ 程序,它可以让我控制整个世界。我已经完成了最终翻译单元的编写,但出现了一个错误:

error C3848: expression having type 'const `anonymous-namespace'::ElementAccumulator<T,BinaryFunction>' would lose some const-volatile qualifiers in order to call 'void `anonymous-namespace'::ElementAccumulator<T,BinaryFunction>::operator ()(const point::Point &,const int &)'
        with
        [
            T=SideCounter,
            BinaryFunction=std::plus<int>
        ]
        c:\program files (x86)\microsoft visual studio 9.0\vc\include\functional(324) : while compiling class template member function 'void std::binder2nd<_Fn2>::operator ()(point::Point &) const'
        with
        [
            _Fn2=`anonymous-namespace'::ElementAccumulator<SideCounter,std::plus<int>>
        ]
        c:\users\****\documents\visual studio 2008\projects\TAKE_OVER_THE_WORLD\grid_divider.cpp(361) : see reference to class template instantiation 'std::binder2nd<_Fn2>' being compiled
        with
        [
            _Fn2=`anonymous-namespace'::ElementAccumulator<SideCounter,std::plus<int>>
        ]

我查看了 binder2nd 的规范,结果是:它需要一个 const AdaptibleBinaryFunction。

所以,没什么大不了的,我想。我只是使用 boost::bind 来代替,对吗?

错误的!现在,我的接管世界的程序编译时间太长(bind 在一个被实例化了很多次的模板中使用)!照这样下去,我的宿敌就要先称霸世界了!我不能让这种事发生——他使用 Java!

那么有人可以告诉我为什么做出这个设计决定吗?这似乎是一个奇怪的决定。我想我现在必须使我的类的一些元素可变...

编辑:有问题的代码:

template <typename T, typename BinaryFunction>
class ElementAccumulator 
    : public binary_function<typename T::key_type, typename T::mapped_type, void>
{
public:
    typedef T MapType;
    typedef typename T::key_type KeyType;
    typedef typename T::mapped_type MappedType;
    typedef BinaryFunction Func;

    ElementAccumulator(MapType& Map, Func f) : map_(Map), f_(f) {}

    void operator()(const KeyType& k, const MappedType& v)
    {
        MappedType& val = map_[k];
        val = f_(val, v);
    }
private:
    MapType& map_;
    Func f_;
};

void myFunc(int n)
{
    typedef boost::unordered_map<Point, int, Point::PointHash> Counter;
    Counter side_count;
    ElementAccumulator<SideCounter, plus<int> > acc(side_count, plus<int>());

        vector<Point> pts = getPts();
    for_each(pts.begin(), pts.end(), bind2nd(acc, n));
}

So, I was writing a C++ program which would allow me to take control of the entire world. I was all done writing the final translation unit, but I got an error:

error C3848: expression having type 'const `anonymous-namespace'::ElementAccumulator<T,BinaryFunction>' would lose some const-volatile qualifiers in order to call 'void `anonymous-namespace'::ElementAccumulator<T,BinaryFunction>::operator ()(const point::Point &,const int &)'
        with
        [
            T=SideCounter,
            BinaryFunction=std::plus<int>
        ]
        c:\program files (x86)\microsoft visual studio 9.0\vc\include\functional(324) : while compiling class template member function 'void std::binder2nd<_Fn2>::operator ()(point::Point &) const'
        with
        [
            _Fn2=`anonymous-namespace'::ElementAccumulator<SideCounter,std::plus<int>>
        ]
        c:\users\****\documents\visual studio 2008\projects\TAKE_OVER_THE_WORLD\grid_divider.cpp(361) : see reference to class template instantiation 'std::binder2nd<_Fn2>' being compiled
        with
        [
            _Fn2=`anonymous-namespace'::ElementAccumulator<SideCounter,std::plus<int>>
        ]

I looked in the specifications of binder2nd and there it was: it took a const AdaptibleBinaryFunction.

So, not a big deal, I thought. I just used boost::bind instead, right?

Wrong! Now my take-over-the-world program takes too long to compile (bind is used inside a template which is instantiated quite a lot)! At this rate, my nemesis is going to take over the world first! I can't let that happen -- he uses Java!

So can someone tell me why this design decision was made? It seems like an odd decision. I guess I'll have to make some of the elements of my class mutable for now...

EDIT: The offending code:

template <typename T, typename BinaryFunction>
class ElementAccumulator 
    : public binary_function<typename T::key_type, typename T::mapped_type, void>
{
public:
    typedef T MapType;
    typedef typename T::key_type KeyType;
    typedef typename T::mapped_type MappedType;
    typedef BinaryFunction Func;

    ElementAccumulator(MapType& Map, Func f) : map_(Map), f_(f) {}

    void operator()(const KeyType& k, const MappedType& v)
    {
        MappedType& val = map_[k];
        val = f_(val, v);
    }
private:
    MapType& map_;
    Func f_;
};

void myFunc(int n)
{
    typedef boost::unordered_map<Point, int, Point::PointHash> Counter;
    Counter side_count;
    ElementAccumulator<SideCounter, plus<int> > acc(side_count, plus<int>());

        vector<Point> pts = getPts();
    for_each(pts.begin(), pts.end(), bind2nd(acc, n));
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

九公里浅绿 2024-09-07 19:39:42

bind (以及旧的、已弃用的 bind1stbind2nd)是值语义的。返回对象是独立的,它不引用参数,无论是否为 const。

要获取引用语义,请将 std::ref 传递给 bind

auto defer_by_value = std::bind( fun, foo, bar ); // Copy fun, foo, and bar.

auto defer_by_ref = std::bind( std::ref( fun ), std::ref( foo ), std::ref( bar ) );
                                                  // Observe fun, foo, and bar.

bind (and the old, deprecated bind1st and bind2nd) are value-semantic. The return object is self-contained and it doesn't reference the parameters, const or not.

To get reference semantics instead, pass std::ref to bind.

auto defer_by_value = std::bind( fun, foo, bar ); // Copy fun, foo, and bar.

auto defer_by_ref = std::bind( std::ref( fun ), std::ref( foo ), std::ref( bar ) );
                                                  // Observe fun, foo, and bar.
梦冥 2024-09-07 19:39:41

binder2nd ctor 采用一个对 AdaptableBinaryFunction 的常量引用——不是一个const <代码>AdaptableBinaryFunction本身。你的实例化代码怎么样?人们通常不会明确提及 binder2nd ,而是通过便利函数 bind2nd (它仅适用于第二个参数 xtypename Operation::second_argument_type(x) 等)。

binder2nd ctor takes a constant reference to an AdaptableBinaryFunction -- not a const AdaptableBinaryFunction per se. How's your instantiating-code? One normally doesn't explicitly mention binder2nd but rather work through convenience function bind2nd (which simply works on the second argument x with a typename Operation::second_argument_type(x) or the like).

-柠檬树下少年和吉他 2024-09-07 19:39:41

好吧,尝试一些推论:

任何东西都需要 const 任何东西的原因是允许某人将 const 任何东西传递给它。

您想要传递给“功能”函数的最明显的 const 内容是对临时变量的引用。

特别是,如果 bind1st 中的其他内容采用非常量引用参数,那么您无法将它们链接在一起以函数式风格进行编程。函数式风格讨厌在一个语句中捕获变量中的临时值,然后“稍后”在“下一个”语句中修改该变量的想法。所有这些都非常必要且有副作用。

不幸的是,这意味着在定义时,函子的operator()在这种情况下需要是const,并且可能在许多其他情况下。你的不是。

boost::bind 是否允许 const 作为相关模板类型的一部分?如果是这样,那么 可能不会这样做,因为 boost::bind 是在人们更加了解如何充分利用模板后设计的。或者也许 bind1st 是用更纯粹的函数式思维设计的,因此没有副作用,因此为什么不应该一切都是 const 呢?我可能错过了问题的部分要点 - 我从您的代码示例中看到为什么要使用参数绑定,但我认为名为 的标头并不明显寻找涉及累加器的任何内容的正确位置;-)

Well, attempting some deduction:

The reason anything takes a const anything, is to permit someone to pass a const anything into it.

The most obvious const something that you want to pass into "functional" functions is a reference to a temporary.

In particular, if bind1st and other stuff in <functional> took a non-const reference parameter, then you couldn't chain them together to program in a functional style. A functional style abhors the idea of capturing a temporary in a variable in one statement, and then "later" modifying that variable in "the next" statement. All very imperative and side-effecty.

Unfortunately, this means that as <functional> is defined, operator() of functors needs to be const in this case and presumably a bunch of other cases. Yours isn't.

Does boost::bind allow either const or not as part of the template type where relevant? If so then maybe <functional> doesn't do this simply because boost::bind was designed once people had more idea how to get the best out of templates. Or maybe bind1st was designed with a purer functional mindset, hence no side-effects, hence why shouldn't everything be const? I may have missed part of the point of the question - I see from your code example why you want to use parameter binding, but I don't think it's obvious that a header called <functional> is the right place to look for anything involving accumulators ;-)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文