2017-11-09 25 views
13

Với g ++ 5.4, đâyTại sao khấu trừ đối số mẫu không thành công cho hàm thành viên?

struct B { 
    void f() {} 
}; 

struct D : public B { 
    void g() {} 
}; 

template <class T> 
void foo(void (T::*)(), void (T::*)()) 
{} 

int main() 
{ 
    foo(&D::f, &D::g); 
} 

thất bại do "loại mâu thuẫn suy luận cho thông số‘T’(‘B’và‘D’)". Tại sao T không được suy ra như D, là một trận đấu chính xác?

+3

Có thể vì không có 'D :: f'? Chỉ tồn tại 'B :: f', vì vậy loại' & D :: f' là 'B :: *'? – geza

Trả lời

12

Ngoài VTT's excellent demonstration. Các văn bản chuẩn trong câu hỏi, tôi tin rằng, là tại [expr.unary.op]/3, tôi nhấn mạnh:

Kết quả của & hành unary là một con trỏ đến toán hạng của nó. Toán hạng phải là một giá trị hoặc một id đủ điều kiện. Nếu toán hạng là id đủ điều kiện đặt tên cho thành viên không tĩnh hoặc biến thể m của một số loại C với loại T, kết quả có loại "con trỏ tới thành viên của loại C loại T" và là giá trị chỉ định C :: m.

id đủ điều kiện bạn sử dụng là D::f, nhưng nó tên một hàm thành viên của B (tôi có thể đưa lên các quy tắc tra cứu nếu bạn muốn). Vì vậy, loại lớp C trong đoạn trên, là B. Do đó, loại này giải quyết thành void (B::*)(void).

+4

Tôi đã không thuyết phục ban đầu, nhưng tôi thấy từ [class.mem] - "Đặc tả thành viên trong một định nghĩa lớp khai báo toàn bộ các thành viên của lớp; không thành viên nào có thể được thêm vào nơi khác." - f đó rõ ràng là một thành viên của B và không phải là thành viên của D, mặc dù D :: nested-name-specifier. – fizzer

13

Loại &D::f sẽ void (B::*)(void)

static_assert(::std::is_same<void (B::*)(void), decltype(&D::f)>::value, ""); 
static_assert(::std::is_same<void (D::*)(void), decltype(&D::f)>::value, ""); // error 
static_assert(::std::is_same<void (D::*)(void), decltype(&D::g)>::value, ""); 

Lý do đằng sau này là nếu không bạn sẽ không thể gán một giá trị cho một biến &D::f của void (B::*)(void) loại mà không có một dàn diễn viên mặc dù f là một thành viên của B hoặc so sánh &D::f == &B::f.

Là một workaround bạn có thể thực hiện một static_cast:

foo(static_cast<void (D::*)(void)>(&D::f), &D::g); 
+0

Đối với tôi, thật khó để chấp nhận lý do. Nếu tôi muốn gán '& D :: f' vào một' B :: * ', thì tôi sẽ gọi nó là' & B :: f'. Hành vi này của C++ là bất ngờ, tôi đã gặp hiện tượng này nhiều lần trong quá khứ. Tại sao kiểu '& D :: f' thay đổi liệu có một' f' thực trong 'D'? Tôi hiểu công việc đằng sau điều này, nhưng không thể chấp nhận nó. Là người dùng 'B' &' D', tôi không quan tâm, người tạo 'B' hay' D' có thêm 'f' vào' D' hay không. – geza

+4

một cách giải quyết dễ dàng hơn sẽ là 'foo (& D :: f, & D :: g)' – CAF

+1

@geza Nhưng cách này '& D :: f == & B :: f' được đánh giá chính xác thành' true' có ý nghĩa hoàn hảo với tôi bởi vì 'f' đề cập đến cùng một hàm trong lớp cơ sở. – VTT

Các vấn đề liên quan