2015-01-28 14 views
7

Có cách nào để buộc trình biên dịch C++ thực hiện tra cứu tên cho một biểu tượng đã cho trong quá trình tạo mẫu hay không?C++ thực thi tra cứu tên truy cập thứ hai trong hàm mẫu

Với đoạn mã sau:

template <class T> 
auto wrapper(T t) -> decltype(f(t)) 
{ 
    return f(t); 
} 

unsigned char f(int x) { return x % 256; } 
unsigned char f(unsigned char x) { return x; } 

int main(int, char **) 
{ 
    auto x = wrapper(3100); 
    return 0; 
} 

Có bất cứ điều gì tôi có thể làm (ngoài di chuyển các định nghĩa của f đầu trang) để làm cho rằng mã biên dịch và đưa ra kết quả tương tự như tất cả các định nghĩa của f có sẵn trước khi định nghĩa của wrapper?

Tôi không thể tìm thấy bất kỳ điều gì, có thể vì tôi không biết cách đặt cụm từ này một cách chính xác. Tất cả các loại đối số của f có thể được giả định là loại do người dùng xác định, nếu điều này có ích.

+0

Tại sao bạn không sử dụng chuyên môn mẫu? – Chiel

+0

@Chiel: Cảm ơn, ý tưởng tuyệt vời! Vì vậy, tôi sẽ thay thế 'f' bằng một mẫu lớp? Điều đó làm việc, nhưng tôi đã hy vọng cho một giải pháp mà sẽ không yêu cầu tôi thay đổi định nghĩa của 'f' (rất nhiều). Hay bạn có điều gì khác trong tâm trí? –

+0

Bạn có thể sử dụng mặc định áp dụng cho hầu hết các trường hợp và chuyên môn khi được yêu cầu, xem câu trả lời của tôi. – Chiel

Trả lời

2

Có một số cách để buộc các trình biên dịch C++ để thực hiện tra cứu tên cho một biểu tượng cho trong mẫu instantiation (và không phải trước đó)?

Có. Trước hết, tên phải phụ thuộc. Tên f trong wrapper khi được sử dụng làm f(t) phụ thuộc vì t phụ thuộc vào loại. [Temp.dep]/1:

Trong một biểu thức có dạng:

                postfix thể hiện (biểu-list opt)

nơi postfix thể hiện là một không đủ tiêu chuẩn-id, các không đủ tiêu chuẩn-id biểu thị một tên phụ thuộc nếu

  • bất kỳ biểu thức trong biểu-list là một mở rộng gói (14.5.3),
  • bất kỳ biểu thức nào trong danh sách biểu thức là cụm từ phụ thuộc vào loại (14.6.2.2), hoặc
  • nếu không đủ tiêu chuẩn-id là một mẫu-id trong đó bất kỳ đối số mẫu phụ thuộc vào một số mẫu.

Vấn đề là tên tuyên bố sau khi mẫu đó, ví dụ: chỉ trong instantiation nhưng không phải là bối cảnh định nghĩa, chỉ có thể được tìm thấy bằng luận tên phụ thuộc tra cứu. f quá tải của bạn chỉ mất loại cơ bản, nhưng những người không có không gian tên toàn cầu liên kết với chúng theo [basic.lookup.argdep]/2:

Nếu T là một loại cơ bản, bộ liên quan của nó trong không gian tên và lớp học đều trống.

Do đó, bạn không bao giờ có thể tìm thấy f nếu đối số có cùng loại với tham số. Một mẹo nhỏ có thể giúp:

template <typename T> 
struct refwrap 
{ 
    T&& t; 
    refwrap(T&& t) : t(std::forward<T>(t)) {} 
    operator T&&() {return std::forward<T>(t);} 
}; 

template <typename T> 
auto make_refwrap(T&& t) -> refwrap<T> // making use of reference collapsing 
{ return {std::forward<T>(t)}; }   // inside refwrap to get forwarding 

mẫu này, khi khai báo trong namespace toàn cục, sẽ gây ra ADL để xem xét nó. Viết lại wrapper như sau:

template <class T> 
auto wrapper(T t) -> decltype(f(make_refwrap(t))) 
{ 
    return f(make_refwrap(t)); 
} 

Demo. Đây không phải là cách thích hợp để làm điều đó mặc dù, vì nó sẽ thất bại trong các kịch bản phức tạp hơn.

+0

Ah, giờ tôi thấy vấn đề ban đầu là gì khi hỏi câu hỏi này. Hàm 'f' của tôi đã được định nghĩa trong một không gian tên khác (tức là giống như' wrapper') rồi đối số. –

+0

Điều khoản nào nói rằng nó chỉ có thể được tìm thấy với ADL? Ngoài ra, những gì sẽ là "thích hợp" cách để làm điều đó? – 0x499602D2

1

Điều này sẽ hoạt động với chuyên môn mẫu. Lưu ý rằng bạn phải quyết định chức năng mặc định là gì, bởi vì tôi không thể nhìn thấy nó trong câu hỏi.

// default function 
template <class T> 
unsigned char f(T x) { return x; } 

// specialization for int 
template <> 
unsigned char f(int x) { return x % 256; } 

int main(int, char **) 
{ 
    auto x = f(3100); 
    return 0; 
} 
+0

Ví dụ của tôi quá đơn giản và có thể bị hiểu sai một cách dễ dàng. Xin đừng cho rằng có * trường hợp mặc định *. Vui lòng không xóa hàm 'wrapper', bởi vì câu hỏi thực sự là về hàm đó, tất nhiên, phức tạp hơn hàm nhận dạng trong tình huống của tôi. –

+0

Tại sao quá tải đơn giản không đủ? – Chiel

+1

Hàm 'wrapper' được cho là sử dụng một hàm' f' khác để có kết quả trung gian và chuyển đổi kết quả đó. Để định nghĩa 'wrapper', tất cả các định nghĩa của' f' phải có sẵn tại thời điểm 'wrapper' được định nghĩa, vì các tên được tra cứu khi phân tích cú pháp định nghĩa mẫu. Đây là hành vi không mong muốn trong trường hợp của tôi, bởi vì người dùng thư viện được cho là có khả năng cung cấp các định nghĩa bổ sung của 'f' và tôi có một thời gian khó khăn để nói với họ rằng chúng phải bao gồm các định nghĩa của chúng trước khi chúng bao gồm một số tệp khác. –

0

Mã sau đây không hoàn toàn sạch sẽ, nhưng minh họa cách sử dụng chuyên biệt mẫu lớp để giải quyết vấn đề. Nó duy trì giao diện ban đầu (ví dụ: fwrapper có thể được sử dụng theo cách tương tự như trước đây).

Cảm ơn bạn đã cho tôi gợi ý đúng. Tôi đang mở cho một giải pháp ít tiết hơn.

#include <type_traits> 

template <class ...> 
struct F; 

template <class T> 
auto wrapper(T t) 
    -> decltype(F<typename std::decay<T>::type>::f(t)) 
{ 
    return F<typename std::decay<T>::type>::f(t); 
} 

template <> 
struct F<unsigned char> 
{ 
    static unsigned char f(unsigned char x) { return x; } 
}; 

template <> 
struct F<int> 
{ 
    static unsigned char f(int x) { return x % 256; } 
}; 

template <class T> 
auto f(T t) 
    -> decltype(F<typename std::decay<T>::type>::f(t)) 
{ 
    return F<typename std::decay<T>::type>::f(t); 
} 

int main(int, char **) 
{ 
    auto x = wrapper(3100); 
    return 0; 
} 
Các vấn đề liên quan