2009-12-16 5 views
6

Xét đoạn mã sauLàm thế nào để lưu con trỏ vào thành viên trong thời gian biên dịch?

template<typename T, int N> 
struct A { 
    typedef T value_type; // OK. save T to value_type 
    static const int size = N; // OK. save N to size 
}; 

Look, nó có thể lưu bất kỳ mẫu tham số nếu tham số này là một typename hoặc một giá trị số nguyên. Vấn đề là con trỏ đến thành viên là một số nguyên, tức là số nguyên. Bây giờ tôi muốn lưu lại bất kỳ con trỏ cho thành viên trong thời gian biên dịch:

struct Foo { 
    int m; 
    int r; 
}; 

template<int Foo::*ptr_to_member> 
struct B { 
    // Next statement DOES NOT WORK! 
    static int Foo::* const saved_ptr_to_member = ptr_to_member; 
}; 

// Example of using 
int main() { 
    typedef B<&Foo::m> Bm; 
    typedef B<&Foo::r> Br; 
    Foo foo; 
    std::cout << (foo.*(Bm::saved_ptr_to_member)); 
} 

Làm thế nào để tiết kiệm con trỏ tới thành viên trong thời gian biên dịch? Tôi sử dụng VS2008.

Lưu ý. Thời gian biên dịch là rất quan trọng. Vui lòng không viết giải pháp thời gian chạy. Tôi biết điều đó.

+2

ha-ha, tôi đoán moonshadow học được cái gì mới hôm nay ... –

+0

@ Jon: thật vậy, chỉ cần phát hiện chapeter 15,5: Con trỏ tới thành viên trong tài liệu tham khảo Stroustrup của tôi. * bẽn lẽn * – moonshadow

+0

tôi vừa gõ một câu từ chính xác phần đó khi tôi nhận ra bạn đã xóa câu trả lời của bạn. Tôi nghĩ rằng tên của họ là khó hiểu mặc dù - họ không thực sự bất cứ điều gì giống như con trỏ. –

Trả lời

0

Tại sao lại sử dụng mẫu?

#include <cstdio> 

struct Foo { 
    int a; 
    int b; 
} foo = {2, 3}; 

int const (Foo::*mp) = &Foo::b; 

int 
main() { 
    printf("%d\n", foo.*mp); 
    return 0; 
} 

Sau đây biên dịch mp này trên gcc-4.4.1 (Tôi không có quyền truy cập vào MSVC vào lúc này):

.globl mp 
     .align 4 
     .type mp, @object 
     .size mp, 4 
mp: 
     .long 4 

Nó chỉ là một bù đắp cho các thành viên, trong đó trông khá thời gian biên dịch với tôi.

Với mẫu, bạn cần phải xác định định nghĩa bên ngoài của lớp:

#include <cstdio> 

struct Foo { 
    int m; 
    int r; 
} foo = {2, 3}; 

template<int Foo::*Mem> 
struct B { 
    static int Foo::* const mp; 
}; 

template<int Foo::*Mem> 
int Foo::* const B<Mem>::mp = Mem; 

int main() { 
    typedef B<&Foo::m> Bm; 
    typedef B<&Foo::r> Br; 
    printf("%d, %d\n", foo.*(Bm::mp), foo.*(Br::mp)); 
} 

nào biên dịch để:

g++ -O2 -S -o- b.cc | c++filt 

... 

     .weak B<&(Foo::r)>::mp 
     .section  .rodata._ZN1BIXadL_ZN3Foo1rEEEE2mpE,"aG",@progbits,B<&(Foo::r)>::mp,comdat 
     .align 4 
     .type B<&(Foo::r)>::mp, @object 
     .size B<&(Foo::r)>::mp, 4 
B<&(Foo::r)>::mp: 
     .long 4 
     .weak B<&(Foo::m)>::mp 
     .section  .rodata._ZN1BIXadL_ZN3Foo1mEEEE2mpE,"aG",@progbits,B<&(Foo::m)>::mp,comdat 
     .align 4 
     .type B<&(Foo::m)>::mp, @object 
     .size B<&(Foo::m)>::mp, 4 
B<&(Foo::m)>::mp: 
     .zero 4 

Tuy nhiên tất cả điều này smacks của thư viện chuẩn tính năng reimplementation (xem std::tr1::mem_fn).

+1

Bạn chỉ không hiểu câu hỏi của tôi. –

+0

Sau đó, hãy làm rõ câu hỏi của bạn, rất nhiều người dường như ở cùng một chiếc thuyền. –

+0

Không được xúc phạm. Tôi muốn lưu con trỏ vào thành viên trong lớp mẫu dưới dạng một loại hoặc một số nguyên. Tôi cần * một con trỏ đến các đặc điểm của thành viên * dưới dạng các đặc điểm kiểu. –

1

Bạn không thể.

Nhưng thay vào đó, bạn có thể sử dụng functionoid. Điều này có thể là một giải pháp biên dịch thời gian. Và bởi vì trình biên dịch có thể nội tuyến mọi thứ, nó thậm chí có thể nhanh hơn một con trỏ tới một hàm thành viên. Ví dụ:

struct Foo { 
    int m; 
    int r; 
}; 

struct FooM { 
    static int call(Foo const &foo) const { return foo.m; } 
} 

struct FooR { 
    static int call(Foo const &foo) const { return foo.r; } 
} 

template<typename FooFun> 
struct B { 
    typedef FooFun foo_fun; 
    int call_foo_fun(Foo const &foo) { return foo_fun::call(foo); } 
}; 

// Example of using 
int main() { 
    typedef B<FooM> Bm; 
    typedef B<FooR> Br; 
    Foo foo; 
    std::cout << Bm.call_foo_fun(foo); 
} 

Chưa được kiểm tra, nhưng bạn có ý tưởng.

0

Bạn không thể khởi tạo thành viên tĩnh bên trong định nghĩa cấu trúc. Nó cần phải được khai báo bên ngoài như thế này (có lẽ không phải là những gì bạn dự định với mẫu, nhưng dù sao):

struct Foo { 
    int m; 
    int r; 
}; 

template<int Foo::*ptr_to_member> 
struct B { 
    static int Foo::* const saved_ptr_to_member; 
}; 

int Foo::* const B<&Foo::m>::saved_ptr_to_member = &Foo::m; 
int Foo::* const B<&Foo::r>::saved_ptr_to_member = &Foo::r; 

// Example of using 
int main() { 
    typedef B<&Foo::m> Bm; 
    typedef B<&Foo::r> Br; 
    Foo foo; 
    std::cout << (foo.*(Bm::saved_ptr_to_member)); 
} 
+0

1) Đây là giải pháp thời gian chạy. 2) Phương pháp này không sử dụng các ưu điểm của mẫu. Tôi phải định nghĩa một con trỏ mới cho thành viên mỗi lần cho trường hợp của một cấu trúc và các thành viên mới. –

+0

1) không, không phải. 2) đúng, nhưng đó là thiết kế ban đầu, bây giờ được viết theo cách mà trình biên dịch hiểu nó. Tôi không thấy bất kỳ lý do gì để sử dụng một mẫu ở đây ngay từ đầu, chỉ cần biến nó thành một lớp với một thành viên không tĩnh và một hàm tạo decend và bạn đã hoàn thành. Đơn giản hơn nhiều ... – user231967

1

Sẽ thật tuyệt khi có giải thích phức tạp hơn về lý do tại sao 'thời gian biên dịch là quan trọng' (giúp đề xuất các giải pháp thay thế). Nhưng với khái niệm của tôi, mọi thứ bạn cần cần để được thực hiện thời gian biên dịch với con trỏ thành viên, bạn thực sự có thể làm. Biến thể của tôi là đề xuất của Thomas pha trộn với một số triết lý sắp xếp của C++. Đầu tiên cho phép xác định:

template <typename T, T v> 
struct val 
{}; 

mẫu struct này hiệu quả có thể phục vụ giá trị thời gian như biên dịch, và bạn không cần phải "giá trị tĩnh = v;", để sử dụng nó hoặc là lúc biên dịch hoặc thời gian chạy.Xem xét:

template <int n> 
struct Foo 
{ 
    //something dependent on n 
}; 

template <typename T> 
struct Bar; 

template <int n> 
struct Bar <val <int, n> > 
{ 
    //something dependent of n 
}; 

Foo và Bar có chức năng tương đương, mỗi mẫu meta-kadabra mà có thể được thực hiện với Foo cũng có thể được thực hiện với Bar (chỉ cần vượt qua val thay vì n). Các vay tương tự như bạn có thể đóng gói con trỏ tới thành viên vào val <>:

val <typeof (&My::a), &My::a> 

những biên dịch giá trị thời gian bây giờ có thể được lưu trữ trong danh sách loại (như boost :: mpl :: cái gì đó), so sánh, chuyển đổi, vv mọi thứ biên dịch thời gian. Và khi bạn cuối cùng sẽ muốn sử dụng chúng như con trỏ-to-thành viên tại thời gian chạy, chỉ cần xác định một hàm mẫu:

template <typename T, T value> 
T 
extract (val <T, value>) 
{ 
    return value; 
} 

và sử dụng nó:

typedef val <typeof (A::i), A::i> a_i; 

A a; 
std::cout << (a .* extract (a_i())); 

PS: có một số cấu trúc vụng về về giải pháp này, nhưng tất cả chỉ vì mục đích đơn giản và giải thích. Ví dụ thay vì xấu xí (a * chiết xuất (a_i()).) Có thể được đơn giản hóa bằng cách gói nó vào một cái gì đó hơn con trỏ-to-thành viên cụ thể:

template <typename M, typename C> 
typename mem_type <M>::value & 
mem_apply (C &c) 
{ 
    M m; 
    return c .* extract (m); 
} 

nơi mem_type là lớp mẫu được chiết xuất loại thành viên giới thiệu bởi M. Sau đó, sử dụng sẽ là:

std::cout << mem_apply <a_i> (a);