2014-04-23 31 views
9

Tôi đang sử dụng GCC 4.8 để biên dịch đoạn mã sau:Tại sao tôi gặp lỗi biên dịch?

#include <memory> 

template<typename T, typename ...Args> 
    std::unique_ptr<T> make_unique(Args&& ...args) { 
    return std::unique_ptr<T>(new T{std::forward<Args>(args)...}); 
} 

struct S { 
    template<class... Args> 
    static std::unique_ptr<S> create(Args&&... args) { 
     return make_unique<S>(std::forward<Args>(args)...); 
    } 
private: // if I remove this line, then the compilation is OK 
    S(int) {} 
    S() = default; 
}; 

int main() { 
    auto s1 = S::create(); // OK 
    auto s2 = S::create(0); // Compilation error 
} 

bất cứ ai có thể giải thích cho tôi lý do của lỗi này từ trình biên dịch?

main.cpp: In instantiation of 'std::unique_ptr make_unique(Args&& ...) [with T = S; Args = {int}]':

main.cpp:11:58: required from 'static std::unique_ptr S::create(Args&& ...) [with Args = {int}]'

main.cpp:20:26: required from here

main.cpp:14:5: error: 'S::S(int)' is private

S(int) {} 
^ 

main.cpp:5:65: error: within this context return std::unique_ptr(new T{std::forward(args)...});

              ^

Trả lời

11

Can anyone explain me the reason of this error from the compiler?

Các nhà xây dựng mà mất int được tuyên bố là private đó là lý do tại sao nó cho lỗi biên dịch. Lưu ý rằng các nhà xây dựng là nhận được gọi từ make_unique (mà không có quyền truy cập cho các thành viên tư nhân), chứ không phải từ create.

Tuy nhiên, có thể bạn đang tự hỏi tại sao cuộc gọi đầu tiên đến create() biên dịch tốt, tôi nghĩ đó là vì GCC có lỗi. Nó không nên biên dịch ngay cả trong trường hợp này, bởi vì hàm tạo mặc định được khai báo là private. Clang cho đúng lỗi cho cả gọi (see this).

Dù sao, nếu bạn muốn giữ chúng private, sau đó thực hiện make_unique một người bạn của lớp học.

2

Trong cấu trúc C++, tất cả thành viên đều được công khai theo mặc định. Trong một khai báo lớp, các thành viên là riêng tư theo mặc định. Trong trường hợp của bạn, nhà thầu đã được thực hiện riêng, đó là lý do tại sao bạn nhận được lỗi: S :: S (int) được tin

Vì vậy, hãy thay đổi như:

public: 
    S(int) {} 
    S() = default; 
+0

Tôi không muốn làm cho các nhà thầu công khai. Đây là lý do tại sao tôi đã giới thiệu tạo(), Hơn nữa, đối với một số lý do chỉ số S() xây dựng tư nhân không làm cho biên soạn thất bại khi nó tư nhân, trong khi S (int) làm cho việc biên soạn thất bại ... – Martin

1

Nếu một số nhà xây dựng là tư nhân, nó có nghĩa là không có ai nhưng chính lớp (và bạn bè) sẽ có thể tạo ra trường hợp của nó bằng cách sử constructor đó.

Để tạo thể hiện của một lớp học mà chỉ có lối đi riêng nhà thầu, bạn phải sử dụng một phương pháp tĩnh.

+1

Đó là chính xác những gì anh ấy đã đang làm... –

4

Lý do khá đơn giản:

Các nhà xây dựng không được gọi từ bên trong S::create, nhưng từ bên trong ::make_unique hàm mẫu, mà không có quyền truy cập vào các chức năng thành viên tin S::S(int).

Sửa lỗi đơn giản là tự gọi số new (xem here).

Trên thực tế, câu hỏi thú vị hơn là lý do tại sao nó không báo lỗi trên cuộc gọi đầu tiên cũng ...

+5

Tôi nghĩ rằng vấn đề của OP là anh ấy (khá hợp lý!) Muốn tránh gọi 'mới' một cách rõ ràng trong mã. –

2

Nếu bạn muốn giữ lại nhà xây dựng của lớp tin, bạn phải thực hiện bất kỳ người sử dụng phi thành viên (ở đây: make_unique) một người bạn:

struct S { 
    template<typename T, typename ...Args> 
    friend std::unique_ptr<T> make_unique(Args&& ...args); 
    // rest as before 
}; 

Ngoài ra, bạn có thể tránh make_unique<> và trực tiếp tạo ra các unique_ptr<S> từ một thành viên tĩnh:

struct S { 
    template<class... Args> 
    static std::unique_ptr<S> create(Args&&... args) 
    { return std::unique_ptr<S>(new S{std::forward<Args>(args)...}); } 
    // rest as before 
}; 
Các vấn đề liên quan