2016-06-20 17 views
14

Đối với struct này:GCC và Clang hành vi khác nhau trên constructor constexpr

struct Wrapper { 
    int value; 

    constexpr explicit Wrapper(int v) noexcept : value(v) {} 
    Wrapper(const Wrapper& that) noexcept : value(that.value) {} 
}; 

Và chức năng này:

constexpr Wrapper makeWrapper(int v) 
{ 
    return Wrapper(v); 
} 

Các mã sau thất bại trong việc biên dịch cho Clang (Apple LLVM phiên bản 7.3.0), nhưng biên soạn tiền phạt cho GCC (4.9+), cả hai đều có -Wall -Wextra -Werror -pedantic-errors:

constexpr auto x = makeWrapper(123); 

Clang phàn nàn rằng không thể sử dụng "Trình tạo gói 'không phải là constexpr' Trình bao bọc 'trong một biểu thức liên tục." Trình biên dịch nào là đúng?

+0

Không có trình biên dịch nào tại thời điểm này, điều gì sẽ xảy ra nếu bạn xóa trình tạo bản sao? – ZaldronGG

+2

Repro với clang3.7 và gcc6.1, MCVE có thể sao chép [ở đây] (http://coliru.stacked-crooked.com/a/d72a2d90f44bb0dc), sửa cho cả hai trình biên dịch [ở đây] (http: //coliru.stacked- crooked.com/a/759e633b484f83a8). –

+0

@Baum mit Augen tại sao không đăng câu trả lời? –

Trả lời

14

Mặc dù bản sao hoặc di chuyển khi trở về Wrapper từ makeWrapper() có thể được ưu tiên, nó được yêu cầu tồn tại với C++ 14. Trình tạo bản sao hiện có là không constexpr và sự tồn tại của nó ngăn cản việc tạo ra một hàm tạo di chuyển ngầm. Kết quả là tôi nghĩ rằng clang là đúng: bạn cần phải làm cho hàm tạo bản sao là constexpr.

Lưu ý rằng với mã C++ 17, mã có thể chính xác: có đề xuất bắt buộc sao chép trong một số ngữ cảnh: P0135r0. Tuy nhiên, có vẻ như sự thay đổi này đã không hạ cánh trong bài báo làm việc. Nó có thể hạ cánh trong tuần này, mặc dù (nhờ @NicolBolas cho chỉ ra rằng nó không phải là có, chưa). Tôi chưa thấy giấy được cập nhật trong số mailing.

+2

Tôi biết bạn đang ở trong Ủy ban ISO C++ (WG21), Xin vui lòng, tôi muốn hỏi: * "Khi nào chúng ta nói một hàm hoặc một constructor là * *** có thể truy cập *** *? "* - bởi vì một trong các yêu cầu cho copy-elision là" có thể truy cập được ". Đọc đoạn kết của [this] (http://eel.is/c++draft/class.copy#32) đoạn – WhiZTiM

+0

@WhiZTiM Tôi nghĩ nó có nghĩa là hàm hoặc hàm tạo tồn tại và có chữ ký bắt buộc (tôi đang sử dụng "chữ ký" lỏng lẻo để có nghĩa là tất cả mọi thứ về hàm bao gồm 'constexpr'-ness và các thứ khác), nhưng trình biên dịch được tự do lựa chọn có thực sự sử dụng nó hay không. Không chắc chắn 100% mặc dù. –

+0

@WhiZTiM: _accessible_ chỉ đề cập đến một thành viên được khai báo [có thể ngầm], không bị xóa, và nó có thể được gọi từ ngữ cảnh tương ứng dựa trên specifier truy cập: constructor có thể tồn tại nhưng được 'private' hoặc' protected 'và cuộc gọi từ bên ngoài lớp học. Hãy xem 12.8 [class.copy] đoạn 30, chỉ 11 [class.access]. Lưu ý rằng phần bạn trích dẫn chỉ đề cập đến phần copy/copy-elision. Phần tiếp theo là 'constexpr'-ness: constructor sao chép có thể truy cập được nhưng nó không phải là' constexpr'. –

4

Clang là chính xác. Nó hoạt động trong g ++ vì nó tự động bỏ qua hàm tạo bản sao (RVO). nếu bạn vượt qua -fno-elide-constructors. g ++ cũng sẽ phàn nàn.

C++ 14 tiêu chuẩn là không rõ ràng về Copy-sự bỏ bớt trong constexpr đối tượng ..

[class.copy/32] ... (phần sao chép ở đây)

Khi các tiêu chuẩn cho sự bỏ bớt các một hoạt động sao chép/di chuyển được đáp ứng .... .... nhà xây dựng được chọn phải có thể truy cập được ngay cả khi cuộc gọi được gọi là .

Cho đến khi chúng tôi biết định nghĩa của accessible? Chúng ta có thể giả sử g ++ cũng đúng?

dcl.constexpr/9

Một specifier constexpr sử dụng trong một tuyên bố đối tượng tuyên bố đối tượng như const. Đối tượng như vậy phải có loại chữ và sẽ được khởi tạo . Nếu nó được khởi tạo bởi một cuộc gọi hàm tạo, cuộc gọi đó phải là một biểu thức không đổi ([expr.const]). Nếu không, hoặc nếu một số chỉ định constexpr được sử dụng trong khai báo tham chiếu, mỗi toàn bộ biểu thức xuất hiện trong trình khởi tạo của nó phải là biểu thức hằng số .

Dietmar Kuhl's answer cho chúng tôi biết điều gì đang diễn ra.

Demo:

struct Wrapper { 
    int value; 

    constexpr explicit Wrapper(int v) noexcept : value(v) {} 
    Wrapper(const Wrapper& that) noexcept : value(that.value) {} 
}; 

constexpr Wrapper makeWrapper(int v) 
{ 
    return Wrapper(v); 
} 

int main() 
{ 
    constexpr auto x = makeWrapper(123); 
} 

Compile với

g++ -std=c++14 -Wall -pedantic -fno-elide-constructors main.cpp && ./a.out 

Xem nó live here

3

Cả hai trình biên dịch là đúng.

Quy tắc cho các chức năng constexpr và bộ khởi tạo nói rằng không thể gọi hàm không constexpr.

Quy tắc sao chép bản sao nói rằng nó không được chỉ định xem liệu trình tạo bản sao non-constexpr có được gọi hay không.

Kết luận duy nhất có thể là không xác định liệu chức năng và trình khởi tạo có đáp ứng các yêu cầu của constexpr hay không. Nếu họ làm, thì trình biên dịch phải chấp nhận nó. Nếu không, thì trình biên dịch phải chẩn đoán vấn đề.

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