tr1::mem_fn и tr1::bind: на const-правильность и перегрузку
Что не так со следующим фрагментом?
#include <tr1/functional>
#include <functional>
#include <iostream>
using namespace std::tr1::placeholders;
struct abc
{
typedef void result_type;
void hello(int)
{ std::cout << __PRETTY_FUNCTION__ << std::endl; }
void hello(int) const
{ std::cout << __PRETTY_FUNCTION__ << std::endl; }
abc()
{}
};
int
main(int argc, char *argv[])
{
const abc x;
int a = 1;
std::tr1::bind(&abc::hello, x , _1)(a);
return 0;
}
Пытаясь скомпилировать его с помощью g++-4.3, кажется, что перегруженные функции cv-qualifier смешивают оба tr1::mem_fn<>
а также tr1::bind<>
и выходит следующая ошибка:
no matching function for call to ‘bind(<unresolved overloaded function type>,...
Вместо этого следующий фрагмент компилируется, но, кажется, нарушает правильность const:
struct abc
{
typedef void result_type;
void operator()(int)
{ std::cout << __PRETTY_FUNCTION__ << std::endl; }
void operator()(int) const
{ std::cout << __PRETTY_FUNCTION__ << std::endl; }
abc()
{}
};
...
const abc x;
int a = 1;
std::tr1::bind( x , _1)(a);
Любая подсказка?
3 ответа
Поиск выполняется в то время, когда постоянство this
не известно Вы просто должны дать ему подсказку через кастинг. Попробуй это:
typedef void (abc::*fptr)(int) const; // or remove const
std::tr1::bind((fptr)&abc::hello, x , _1)(a);
Вы также можете заметить, что удаление const
еще работает. Это потому, что вы должны передавать x по указателю (потому что первый аргумент функции-члена C++, неявный this
параметр, всегда указатель). Попробуйте это вместо этого:
typedef void (abc::*fptr)(int) const; // won't compile without const (good!)
std::tr1::bind((fptr)&abc::hello, &x , _1)(a);
Как обнаружилось во время моих комментариев ниже, если вы опустите &
как вы изначально делали, вы будете передавать x по значению, что обычно не то, что вы хотите (хотя в вашем конкретном примере это мало что меняет). Это на самом деле кажется неудачной ловушкой для bind
,
Как предположил Джон, проблемы, возникающие в этих фрагментах, следующие:
- При передаче указателя на функцию-член необходимо указать его подпись (если она перегружена)
bind()
аргументы передаются по значению.
Первая проблема решается путем приведения указателя на функцию-член, предоставленного для связывания:
std::tr1::bind(static_cast< void(abc::*)(int) const >(&abc::hello), x, _1)(a);
Второе может быть решено путем передачи вызываемого объекта по адресу (как предложил Джон) или с помощью TR1 reference_wrapper<>
- в противном случае оно будет передано по значению, что приведет к нарушению галлюцинации.
Данный вызываемый объект:
std::tr1::bind( std::tr1::ref(x) , _1)(a);
bind()
будет пересылать a
к правильному operator()
в соответствии с х постоянством.
На этот вопрос ответили, но я считаю, что лучший способ указать перегрузку с помощью bind - это указать ее в шаблоне:
std::tr1::bind<void(foo::*)(int)>(&foo::bar);
Этот метод такой же явный, но короче, чем приведение static_cast
тем не мение. Но он чище, чем C-cast, такой же длины.