2017-02-08 5 views
9

У меня есть следующий фрагмент кода:result_of не определяет тип для mem_fn

#include <functional> 

struct X { 
    int get() const& { 
     return 42; 
    } 
}; 

template<typename Func> 
std::result_of_t<Func(X)> Apply(Func fn) { 
    X x; 
    return fn(x); 
} 

int main(void) { 
    Apply([](X const& x){return x.get();}); 
    //Apply(std::mem_fn(&X::get)); // does not compile 
} 

Первый вызов Apply компилируется нормально, но если я раскомментировать второй вызов, я получаю следующее сообщение об ошибке компиляции:

main.cpp:16:5: error: no matching function for call to 'Apply' 
    Apply(std::mem_fn(&X::get)); // does not compile 
    ^~~~~ 
main.cpp:10:27: note: candidate template ignored: substitution failure [with Func = std::_Mem_fn<int (X::*)() const &>]: no type named 'type' in 'std::result_of<std::_Mem_fn<int (X::*)() const &> (X)>' 
std::result_of_t<Func(X)> Apply(Func fn) { 
         ^

Я как-то ожидал, что оба вызова могут использоваться взаимозаменяемо, и что std::mem_fn просто «пойдет правильно». Может ли кто-нибудь объяснить, что здесь происходит?

ответ

7

Проблемы здесь:

int get() const& { 
//   ^^^ 

Вашей функция члена именующая ссылка квалифицирована. В вашем Apply():

template<typename Func> 
std::result_of_t<Func(X)> Apply(Func fn) { 
    return fn(X{}); 
} 

вы вызываете его с RValue. Что приводит нас к [очень удивительно для меня] разница между этими двумя выражениями:

X{}.get();  // ok 
(X{}.*&X::get)(); // ill-formed 

На специально операторов указатель на член, реф-классификаторы указателя членов сверяются категории ценностей объект. Из [expr.mptr.oper]:

In a .* expression whose object expression is an rvalue, the program is ill-formed if the second operand is a pointer to member function with ref-qualifier& . In a .* expression whose object expression is an lvalue, the program is ill-formed if the second operand is a pointer to member function with ref-qualifier && .

Таким образом, первое выражение в порядке, get() является const& -qualified но rvalues ​​может связываться с этим. Второе выражение не в порядке - правила прямо запрещают его.

Таким образом, поведение, которое вы видите, совершенно верно - mem_fn определяется непосредственным вызовом функции-члена, которая плохо сформирована на rvalue, поэтому Apply удаляется из набора перегрузки. Если бы это не так, то создание экземпляра тела было бы трудной ошибкой.

Причина, по которой работает лямбда, заключается в том, что временный X привязан к эталонному параметру лямбда. Затем вызывается get() по параметру функции lvalue, а не по временному, переданному в него. Но даже без этого, ссылаясь на get() непосредственно на временное все равно будет хорошо.

+0

Предлагаю детализировать, почему тоже работает лямбда :) – Quentin

+2

'const &' связывается с rvalues. –

+0

@ T.C. да, но ref-qualifiers фактически не связаны с привязкой ссылки - они напрямую ограничивают категорию вызывающего объекта. – Quentin

Смежные вопросы