2011-11-18 24 views
8

Khi được hỏi vì này: Default argument in c++Làm cách nào tôi có thể chỉ định rõ ràng đối số nào tôi đang truyền và các đối số nào vẫn là mặc định?

Nói rằng tôi có một chức năng như thế này: void f(int p1=1, int p2=2, int p3=3, int p4=4);

Và tôi muốn gọi nó là chỉ sử dụng một số các đối số - phần còn lại sẽ là giá trị mặc định.

Something như thế này sẽ làm việc:

template<bool P1=true, bool P2=true, bool P3=true, bool P4=true> 
void f(int p1=1, int p2=2, int p3=3, int p4=4); 
// specialize: 
template<> 
void f<false, true, false, false>(int p1) { 
    f(1, p1); 
} 
template<> 
void f<false, true, true, false>(int p1, int p2) { 
    f(1, p1, p2); 
} 
// ... and so on. 
// Would need a specialization for each combination of arguments 
// which is very tedious and error-prone 

// Use: 
f<false, true, false, false>(5); // passes 5 as p2 argument 

Nhưng nó đòi hỏi quá nhiều mã phải thực tế.

Có cách nào tốt hơn để thực hiện việc này không?

+0

chính xác bạn muốn đạt được điều gì? tôi có cảm giác bất cứ điều gì bạn đang cố gắng làm có thể được thực hiện một cách đơn giản hơn. đoạn mã trên trông giống như khởi đầu của một cơn ác mộng bảo trì. –

+0

@AndersK. Một cách để xác định đối số nào tôi muốn sử dụng. Tôi đồng ý rằng nó không nên được thực hiện, nhưng tôi vẫn còn tò mò nếu có thể. – Pubby

+1

Bạn có thực sự cần nó? Bạn sẽ phải xem xét (N!) Biến thể của các đối số đặt. 4! = 24 định nghĩa. It's * khá * rõ ràng hơn để vượt qua tất cả các thông số –

Trả lời

11

Sử dụng Named Parameters Idiom (→ FAQ link).

Các Boost.Parameters thư viện (→ link) cũng có thể giải quyết nhiệm vụ này, nhưng trả tiền cho theo mã tính cách rườm rà và giảm đáng kể rõ ràng. Nó cũng thiếu xử lý các nhà xây dựng. Và nó đòi hỏi phải có thư viện Boost được cài đặt, tất nhiên. .

Cheers & hth,

+0

Thành ngữ thông số được đặt tên chỉ khác về cú pháp so với một loạt các hàm setter. IMHO thứ hai là tự nhiên hơn, như chức năng chuỗi gọi mùi xấu. Cả hai cách tiếp cận có cùng một vấn đề, tạo kiểu init của các hàm như là một thay thế cho khởi tạo ctor. –

+1

Hãy nhớ rằng các tham số tạo ra một số thông báo lỗi tồi tệ nhất mà bạn sẽ thấy từ trình biên dịch, nếu bạn sử dụng nó cho mã mẫu. – pmr

7

Hãy xem thư viện Boost.Parameter.

Nó triển khai các tham số có tên trong C++. Ví dụ:

#include <boost/parameter/name.hpp> 
#include <boost/parameter/preprocessor.hpp> 
#include <iostream> 

//Define 
BOOST_PARAMETER_NAME(p1)  
BOOST_PARAMETER_NAME(p2) 
BOOST_PARAMETER_NAME(p3) 
BOOST_PARAMETER_NAME(p4) 

BOOST_PARAMETER_FUNCTION(
         (void), 
         f, 
         tag, 
         (optional    
          (p1, *, 1) 
          (p2, *, 2) 
          (p3, *, 3) 
          (p4, *, 4))) 
{ 
    std::cout << "p1: " << p1 
      << ", p2: " << p2 
      << ", p3: " << p3 
      << ", p4: " << p4 << "\n"; 
} 
//Use 
int main() 
{ 
    //Prints "p1: 1, p2: 5, p3: 3, p4: 4" 
    f(_p2=5); 
} 
+1

+1. Điều đó có vẻ thú vị. Cảm ơn các liên kết. :-) – Nawaz

+0

Tôi thích câu trả lời này rất nhiều, mặc dù đó là một chút quá mức cần thiết. – Pubby

+0

Boost.Parameters là khá một chút quá mức cần thiết cho nhiệm vụ. –

2

Đây không phải là thực sự là một câu trả lời, nhưng ...

Trong C++ Template Metaprogramming David Abrahams và Aleksey Gurtovoy (! Xuất bản năm 2004) các tác giả nói về điều này:

Khi viết cuốn sách này, chúng tôi xem xét lại giao diện được sử dụng để đặt tên hỗ trợ thông số chức năng. Với một chút thử nghiệm chúng tôi phát hiện ra rằng nó có thể cung cấp cú pháp lý tưởng bằng cách sử dụng đối tượng từ khóa với toán tử gán quá tải:

f(slew = .799, name = "z"); 

Họ tiếp tục nói:

Chúng tôi không sẽ đi sâu vào chi tiết triển khai của thư viện tham số ở đây; đủ đơn giản để chúng tôi đề xuất bạn hãy thử tự mình triển khai như một bài tập.

Điều này nằm trong bối cảnh lập trình meta mẫu và Boost :: MPL. Tôi không quá chắc chắn như thế nào thực hiện "straighforward" của họ sẽ jive với các thông số mặc định, nhưng tôi giả sử nó sẽ được minh bạch.

+0

Đây thực sự là trung tâm của phép thuật đằng sau Boost.Parameters. Tôi đã sử dụng thành công cho DSLs là tốt, nó thực sự tạo ra giao diện gọn gàng. –

+0

@MatthieuM. Vì vậy, tất cả tôi phải làm là đọc các tiêu đề Boost.Parameters để tìm hiểu điều này (black?) Ma thuật? Trong khi tôi đang ở đó, tôi sẽ nghĩ rằng tôi sẽ đọc tất cả * các nguồn Boost. Sau đó, tôi có thể là một lập trình viên C++ tốt ở tuổi 70. :) –

+0

Thành thật mà nói, tôi không thể nói tôi muốn sử dụng boost.parameters, đó là một điều kỳ lạ, nhưng trong thực tế việc chữa bệnh tệ hơn là căn bệnh này. Tôi sẽ sử dụng tạo mã hoặc (nếu hiệu suất không quan trọng) một vài dòng phân tích cú pháp đơn giản và cung cấp một chuỗi cho một hàm: 'f (" slew = .799, name = \ "z \" ");' –

4

Mặc dù Boost.Thông số là thú vị, nó phải chịu đựng (không may) đối với một số vấn đề, trong đó giữ chỗ va chạm (và cần phải gỡ lỗi kỳ quặc preprocessors/mẫu lỗi):

BOOST_PARAMETER_NAME(p1) 

sẽ tạo _p1 giữ chỗ mà bạn sau đó sử dụng sau này. Nếu bạn có hai tiêu đề khác nhau khai báo cùng một trình giữ chỗ, bạn sẽ gặp phải xung đột. Không vui.

Có một câu trả lời đơn giản hơn (cả khái niệm và thực tế), dựa trên mẫu Builder phần nào là Các tham số có tên Idiom.

Thay vì chỉ định một chức năng như:

void f(int a, int b, int c = 10, int d = 20); 

Bạn chỉ rõ một cấu trúc, mà trên đó bạn sẽ ghi đè lên operator():

  • các nhà xây dựng được sử dụng để yêu cầu đối số bắt buộc (không chặt chẽ trong thành ngữ tham số được đặt tên, nhưng không ai nói rằng bạn phải theo dõi nó một cách mù quáng) và các giá trị mặc định được đặt cho các tùy chọn
  • mỗi thông số tùy chọn được cung cấp cho một setter

Nói chung, nó được kết hợp với Chaining bao gồm việc đặt các bộ định tuyến trả về tham chiếu đến đối tượng hiện tại để các cuộc gọi có thể được ghép trên một dòng.

class f { 
public: 
    // Take mandatory arguments, set default values 
    f(int a, int b): _a(a), _b(b), _c(10), _d(20) {} 

    // Define setters for optional arguments 
    // Remember the Chaining idiom 
    f& c(int v) { _c = v; return *this; } 
    f& d(int v) { _d = v; return *this; } 

    // Finally define the invocation function 
    void operator()() const; 

private: 
    int _a; 
    int _b; 
    int _c; 
    int _d; 
}; // class f 

Các gọi là:

f(/*a=*/1, /*b=*/2).c(3)(); // the last() being to actually invoke the function 

Tôi đã nhìn thấy một biến thể đặt các đối số bắt buộc như tham số cho operator(), điều này tránh việc giữ các đối số như là thuộc tính nhưng cú pháp là kì quặc một chút:

f().c(3)(/*a=*/1, /*b=*/2); 

Khi trình biên dịch đã inlined tất cả các hàm tạo và gọi hàm (đây là lý do tại sao chúng được định nghĩa ở đây, trong khi operator() thì không), nó nên res ult trong mã hiệu quả tương tự so với lời gọi hàm "thường xuyên".

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