странная ошибка компилятора с использованием bind2nd(): функция-член уже определена или объявлена ​​вместо ссылки на ссылку

Недавно я потратил довольно много времени на понимание сообщения об ошибке при вызове func() в этом фрагменте кода:

int main()
{
    vector< vector<double> > v;

    double sum = 0;
    for_each( v.begin(), v.end(), 
        bind2nd( ptr_fun(func), &sum ) );

    return 0;
}

когда func() было объявлено так, код компилировался нормально:

void func( vector<double> v, double *sum )
{
}

когда я использовал это объявление (для эффективности), я получил ошибку компилятора:

void func( const vector<double> &v, double *sum )
{
}

Ошибка, которую я ожидал увидеть, была чем-то вроде ошибки ссылки на ссылку из-за определения operator() в binder2nd,

result_type operator()(const argument_type& _Left) const

Вместо этого, к моему удивлению, компилятор Visual C++ (VS2012) выдал следующую ошибку:

ошибка C2535: 'void std::binder2nd‹_Fn2>::operator ()(const std::vector‹_Ty> &) const': функция-член уже определена или объявлена

который я не могу расшифровать.

  • Можете ли вы объяснить, по какому механизму operator() уже определено?

Полная ошибка, которую я получил, была:

error C2535: 'void std::binder2nd<_Fn2>::operator ()(const std::vector<_Ty> &) const' : member function already defined or declared
with
[
     _Fn2=std::pointer_to_binary_function<const std::vector<double> &,double *,void,void (__cdecl *)(const std::vector<double> &,double *)>,
      _Ty=double
]
c:\vc\include\xfunctional(319) : see declaration of 'std::binder2nd<_Fn2>::operator ()' 
with
[
      _Fn2=std::pointer_to_binary_function<const std::vector<double> &,double *,void,void (__cdecl *)(const std::vector<double> &,double *)>
] 
c:\consoleapplication1.cpp(31) : see reference to class template instantiation 'std::binder2nd<_Fn2>' being compiled 
with 
[
       _Fn2=std::pointer_to_binary_function<const std::vector<double> &,double *,void,void (__cdecl *)(const std::vector<double> &,double *)>
]

Build FAILED.

person Grim Fandango    schedule 10.09.2012    source источник
comment
Поскольку вы используете VS 2012, я думаю, вы могли бы просто переключиться на С++ 11 и использовать lambdas/std::bind, чтобы избежать этих устаревших вещей.   -  person kennytm    schedule 10.09.2012
comment
похоже, что это относится к op() структуры binder2nd, которая из-за свертывания ссылок (или аналогичного) была определена дважды с одной и той же подписью.   -  person PlasmaHH    schedule 10.09.2012
comment
Это довольно интересно. Казалось бы, в старых обертках биндеров нельзя иметь ссылочные типы аргументов?!   -  person Kerrek SB    schedule 10.09.2012


Ответы (2)


Это поведение четко определено (каждый правильный компилятор C++ не сможет скомпилировать ваш код).

Из стандартного (N3376) раздела D.9.3 шаблона класса binder2nd существуют следующие два определения operator():

typename Fn::result_type
operator()(const typename Fn::first_argument_type& x) const;

typename Fn::result_type
operator()(typename Fn::first_argument_type& x) const;

Если first_argument_type уже является const T&, они будут конфликтовать.

person Travis Gockel    schedule 10.09.2012
comment
+1, похоже, это даже проблема, если first_argument_type уже является T&? По крайней мере, я обнаружил, что std::bind2nd(std::ptr_fun(f), "xyz"); не компилируется с той же ошибкой, если f равно void f(std::ostream &str, const char *s);. Увы, передавать std::ostream по значению не вариант, поэтому, похоже, вам придется прибегнуть к передаче указателя на него. :-/ - person Frerich Raabe; 03.07.2014

Это не ответ, но я просто хочу записать современное решение С++ 11, в котором все небольшие помощники связывания устарели в пользу универсального std::bind:

#include <functional>
#include <vector>
#include <algorithm>

void func(std::vector<double> const & v, double * sum) { /* ... */ }

int main()
{
    std::vector<std::vector<double>> v;
    double sum = 0;
    std::for_each(v.begin(), v.end(), std::bind(func, std::placeholders::_1, &sum));
}

Вариативные шаблоны C++11, а также более полный набор свойств, изменяющих тип, дают std::bind гораздо более сильные возможности вывода, чем предыдущие компоненты в <functional>.

person Kerrek SB    schedule 10.09.2012