2016-03-17 19 views
14

Đây là mã của tôi:Có thể sử dụng cuộc gọi hàm thành viên làm đối số mặc định không?

struct S 
{ 
    int f() { return 1; } 
    int g(int arg = f()) { return arg; } 
}; 

int main() 
{ 
    S s; 
    return s.g(); 
} 

này thất bại trong việc biên dịch với các lỗi:

error: cannot call member function 'int S::f()' without object 

Cố this->f() không hoạt động, hoặc, như this có thể không được sử dụng trong bối cảnh đó.

Có cách nào để thực hiện công việc này, vẫn đang sử dụng đối số mặc định không?


Tất nhiên nó có thể được giải quyết bằng cách không sử dụng đối số mặc định ở tất cả:

int g(int arg) { return arg; } 
int g() { return g(f()); } 

tuy nhiên đó được tiết xem xét rằng trong "mã thực" có nhiều thông số trước khi arg, và một số các chức năng theo mẫu này. (Và thậm chí xấu hơn nếu có nhiều đối số mặc định trong một hàm).

NB. This question trông tương tự lúc đầu, nhưng trên thực tế, ông đang yêu cầu làm thế nào để hình thành một đóng cửa, đó là một vấn đề khác nhau (và các giải pháp liên kết không áp dụng cho tình hình của tôi).

+0

Nó phải là 'int g() {return g (f()); } ', phải không? Ít nhất, trong mã thực, nó sẽ có ý nghĩa nhiều. Tất nhiên, ở đây nó làm việc cho bạn chỉ có câu lệnh 'return' trong' g'. – skypjack

+0

@skypjack cảm ơn, cố định –

Trả lời

12

Bạn chỉ có thể sử dụng thành viên ở đó nếu họ là static. Từ bản nháp C++ 11 (n3299), §8.3.6/9:

Similarly, a non-static member shall not be used in a default argument, even if it is not evaluated, unless it appears as the id-expression of a class member access expression (5.2.5) or unless it is used to form a pointer to member (5.3.1).

ví dụ: công trình này:

struct S { 
    static int f() { return 1; } 
    int g(int arg = f()) { return arg; } 
}; 

int main() 
{ 
    S s; 
    return s.g(); 
} 

này cũng làm việc (tôi nghĩ đó là những gì biểu hiện đầu tiên có nghĩa):

struct S { 
    int f() { return 42; } 
    int g(int arg); 
}; 

static S global; 

int S::g(int arg = global.f()) { return arg; } 

int main() 
{ 
    S s; 
    return s.g(); 
} 

Đối với this, nó thực sự là không được phép (§8.3.6/8):

The keyword this shall not be used in a default argument of a member function.

Trang default arguments trên cppreference.com có ​​rất nhiều chi tiết liên quan đến máy in phun - nó có thể khá phức tạp.

+1

Ngoài ra, "Từ khóa' this' sẽ không được sử dụng trong đối số mặc định của hàm thành viên. " – Jaege

+0

Cảm ơn @Jaege, cũng đã thêm báo giá đó. – Mat

5

Nếu bạn được phép sử dụng các tính năng thử nghiệm từ C++ 17, bạn có thể sử dụng std::optional từ STL (xem here để biết thêm chi tiết).

Nói cách khác cái gì đó như:

int g(std::optional<int> oarg = std::optional<int>{}) { 
    int arg = oarg ? *oarg : f(); 
    // go further 
} 

EDIT

Như đã đề cập trong các ý kiến, các mã trên nên logic tương đương với một dưới đây:

int g(std::optional<int> oarg = std::optional<int>{}) { 
    int arg = oarg.value_or(f()); 
    // go further 
} 

này một là dễ đọc hơn một chút (phải không?), nhưng xin lưu ý rằng nó thực hiện f trong mọi trường hợp.
Nếu chức năng đó đắt tiền, có thể nó không đáng giá.

+0

Rất tiếc, trong mã thực sự của tôi, đối số được lấy bởi tham chiếu const, chứ không phải theo giá trị. Tôi đã không nhận ra điều này sẽ tạo sự khác biệt lúc đầu. Tôi nghĩ rằng giải pháp của bạn vẫn sẽ áp dụng mặc dù (có thể thay đổi loại gói để 'reference_wrapper' hoặc một cái gì đó) –

+0

@ M.M Yeah, nó vẫn được áp dụng. Dù sao, tôi đã thêm một câu trả lời mà cung cấp có thể là một giải pháp tốt hơn (ít nhất, một điểm mà từ đó để bắt đầu). Hãy cho tôi biết nếu đó là một cách tiếp cận phù hợp với bạn. – skypjack

+0

Chỉ là một gợi ý nhỏ - tôi nghĩ rằng 'value_or' nên được sử dụng thay vì'?: 'Trong các trường hợp như thế này. – Predelnik

3

Tôi thêm một câu trả lời khác, câu trả lời hoàn toàn khác với câu trả lời trước và có thể giải quyết được sự cố của bạn.
Ý tưởng là sử dụng một lớp khác và kết hợp đúng của các nhà thầu rõ ràng và không rõ ràng.
Nó sau một tối thiểu, ví dụ làm việc:

#include <functional> 
#include <iostream> 

template<class C, int(C::*M)()> 
struct Arg { 
    std::function<int(C*)> fn; 
    Arg(int i): fn{[i](C*){ return i; }} { } 
    explicit Arg(): fn{[](C* c){ return (c->*M)(); }} { } 
}; 

struct S { 
    int f() { return 1; } 
    int h() { return 2; } 
    void g(int arg0, 
      Arg<S, &S::f> arg1 = Arg<S, &S::f>{}, 
      Arg<S, &S::h> arg2 = Arg<S, &S::h>{}) 
    { 
     std::cout << "arguments" << std::endl; 
     std::cout << "arg0: " << arg0 << std::endl; 
     std::cout << "arg1: " << arg1.fn(this) << std::endl; 
     std::cout << "arg2: " << arg2.fn(this) << std::endl; 
    } 
}; 

int main() { 
    S s{}; 
    s.g(42, 41, 40); 
    s.g(0); 
} 

Ví dụ cho thấy cách bạn có thể kết hợp cả hai thông số mặc định và những người không định sẵn.
Nó khá đơn giản để sửa đổi nó và để g là một hàm có danh sách đối số trống, như trong câu hỏi ban đầu.
Tôi cũng khá chắc chắn rằng người ta có thể tinh chỉnh các ví dụ và kết thúc với một cái gì đó tốt hơn thế, dù sao nó phải là một điểm tốt mà từ đó để bắt đầu.

Nó sau các giải pháp áp dụng cho ví dụ ban đầu từ câu hỏi:

#include <functional> 

template<class C, int(C::*M)()> 
struct Arg { 
    std::function<int(C*)> fn; 
    Arg(int i): fn{[i](C*){ return i; }} { } 
    explicit Arg(): fn{[](C* c){ return (c->*M)(); }} { } 
}; 

struct S { 
    int f() { return 1; } 
    int g(Arg<S, &S::f> arg = Arg<S, &S::f>{}) { 
     return arg.fn(this); 
    } 
}; 

int main() { 
    S s{}; 
    return s.g(); 
} 

Và đó là tất cả, nó có thể làm điều đó, ngay cả khi không static phương pháp hoặc các biến toàn cầu.
Tất nhiên, chúng tôi có thể sử dụng bằng cách nào đó. Đó là vấn đề uốn ngôn ngữ một chút ...

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