2016-04-01 31 views
8

Nó rất dễ dàng để giới thiệu các nhà khai thác trung tố mới trong C++khai thác ghi vào người dùng định nghĩa

// User-defined infix operator framework 

template <typename LeftOperand, typename Operation> 
struct LeftHelper 
{ 
    const LeftOperand& leftOperand; 
    const Operation& operation; 
    LeftHelper(const LeftOperand& leftOperand, 
       const Operation& operation) 
     : leftOperand(leftOperand), operation(operation) {} 
}; 

template <typename LeftOperand, typename Operation > 
auto operator < (const LeftOperand& leftOperand, 
       Operation& operation) 
{ 
    return LeftHelper<LeftOperand, Operation>(leftOperand, operation); 
} 

template <typename LeftOperand, typename Operation, typename RightOperand> 
auto operator > (LeftHelper<LeftOperand, Operation> leftHelper, 
       const RightOperand& rightOperand) 
{ 
    return leftHelper.operation(leftHelper.leftOperand, rightOperand); 
} 

// Defining a new operator 

#include <cmath> 
static auto pwr = [](const auto& operand1, const auto& operand2) { return std::pow(operand1, operand2); }; 

// using it 
#include <iostream> 
int main() 
{ 
    std::cout << (2 <pwr> 16) << std::endl; 
    return 0; 
} 

Live demo

Thật không may, điều hành quyền lực này có độ ưu tiên sai và associativity. Vì vậy, câu hỏi của tôi là: làm thế nào để sửa lỗi này? Tôi muốn số <pow> của mình có mức ưu tiên cao hơn * và liên kết ở bên phải, giống như ký hiệu toán học.

Chỉnh sửa Có thể thay đổi mức độ ưu tiên bằng cách sử dụng các dấu ngoặc khác nhau, ví dụ: |op|, /op/, *op* hoặc thậm chí, nếu một trong số đó nghiêng, <<--op-->>, nhưng không thể cao hơn giá trị ưu tiên cao nhất được tích hợp sẵn theo cách này. Nhưng ngày nay C++ là rất mạnh mẽ với lập trình meta mẫu và loại khấu trừ, chỉ đơn giản là phải có một số cách khác để đạt được kết quả mong muốn.

Ngoài ra, sẽ rất tuyệt nếu tôi có thể sử dụng pow và không phải pwr. Thật không may trong một số triển khai #include <cmath> mang pow vào không gian tên chung, do đó sẽ có xung đột. chúng ta có thể quá tải operator not như vậy mà một lời tuyên bố của mẫu

not using std::pow; 

loại bỏ std::pow từ không gian tên toàn cầu?

Đọc thêm: a related proposal by Bjarne Stroustrup.

+6

"dễ dàng" .......... ha ha. (như, nghiêm túc, tôi hy vọng đây chỉ là mục đích nghiên cứu) –

+7

Bạn * không thể * "phát minh" toán tử mới, bạn chỉ cần sử dụng toán tử hiện tại để mô phỏng một cái gì đó hoạt động tương tự như toán tử nhưng không phải là toán tử thực tế. Đó là lý do tại sao các nhà điều hành ưu tiên không hoạt động. Và để trả lời câu hỏi của bạn, không có cách nào để sửa lỗi này. –

+2

Đây là một ý tưởng tương tự với toán tử "chạy tới": 'while (i ----> 0)'. Rất tiện dụng! – user2079303

Trả lời

6

Nguyên tắc gây ngạc nhiên ít nhất là quan trọng và điều quan trọng là a*b *power* c * d đánh giá là a* (b^c) *d. May mắn có một giải pháp dễ dàng.

Để đảm bảo rằng *power* có mức ưu tiên cao hơn phép nhân, bạn phải sử dụng kỹ thuật toán tử có tên tương tự cho phép nhân.

Sau đó thay vì trực tiếp tính toán kết quả của *power**times*, bạn thay vì xây dựng một cây biểu thức. Cây biểu thức này, khi được đánh giá, có thể áp dụng quy tắc ưu tiên tùy ý.

Chúng ta có thể làm điều này với tất cả built-in điều hành, cho chúng ta một cách dễ dàng để đọc cú pháp cho phép thời gian biên dịch lập trình meta của nhà khai thác được ưu tiên:

auto z =equals= bracket< 
    a *plus* b *times* c *power* bracket< 
    a *plus* b 
    >bracket *power* x *times* y 
>bracket; 

Để tránh mẫu biểu này được lưu trữ lâu hơn tối ưu , chỉ cần quá tải operator auto()&& để trả về loại suy luận. Nếu trình biên dịch của bạn không hỗ trợ tính năng đó, =equals= có thể trả về loại thích hợp với chi phí rõ ràng.

Lưu ý rằng cú pháp trên thực sự có thể thực hiện được trong C++ sử dụng các kỹ thuật tương tự như của OP. Việc triển khai thực tế lớn hơn một bài đăng SO nên chứa.

Có những lợi ích khác.Như mọi người đều biết, các ký tự ASCII che khuất trong các ngôn ngữ lập trình đã rơi ra khỏi lợi, và người đọc C++ có thể được confuesed bởi các biểu thức như:

int z = (a + b* pow(c,pow(x,a+b))*y); 

Với kỹ thuật này, tất cả các nhà khai thác có tên có thể đọc được mà làm cho ý nghĩa của chúng rõ ràng, và tất cả mọi thứ được thực hiện infix thay vì trộn infix và ký hiệu tiền tố.

Các giải pháp tương tự để đảm bảo rằng pow có sẵn có thể được thực hiện bằng cách thực hiện lại <cmath> làm <cmath_nopow> chính bạn. Điều này tránh các toán tử quá tải không phải trên các cấu trúc ngôn ngữ, làm cho các mona ngữ pháp AST tách rời và/hoặc vi phạm tiêu chuẩn. Có thể thử Haskell?

+1

Đây là một ý tưởng tuyệt vời. Nếu một người luôn sử dụng 'dấu ngoặc// .../ngoặc vuông' để đặt ngoặc ở cấp cao nhất, thậm chí có thể giữ lại ký pháp' * 'chuẩn cho phép nhân! Đối với Haskell ... Tôi không biết, có lẽ để cho Snoyman [sửa chữa một trong hai đơn nguyên đầu tiên] (http://www.yesodweb.com/blog/2016/04/fixing-monad-either)? –

+1

@ n.m. Tôi không biết, 'khung /' trông có vẻ khó hiểu. Chúng ta có thể sử dụng 'bra <' and '> ket', phù hợp với việc sử dụng trước đó trong cơ học lượng tử. – Yakk

+0

Nếu sử dụng toán tử một ký tự cũ như '*', bạn cần dấu ngoặc với mức ưu tiên cao nhất có thể, do đó, 'bra < > ket', hấp dẫn như chúng sẽ không hoạt động. Cặp 'bra// ket' có lẽ là điều tốt nhất tiếp theo. –

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