2016-01-01 20 views
16

Số constexpr có chỉ định số noexcept thông số cho hàm không? Answer đến the similar question nói "có" liên quan đến inline thông số kỹ thuật, nhưng Eric Niebler's article làm cho tôi tự hỏi về câu trả lời có thể cho câu trả lời hiện tại. Câu trả lời của tôi có thể phụ thuộc vào ngữ cảnh của việc sử dụng hàm constexpr: là ngữ cảnh biểu thức liên tục hoặc ngữ cảnh thời gian chạy, tức là tất cả các tham số của hàm được biết tại thời gian biên dịch hay không.Liệu constexpr ngụ ý không nhận diện?

Tôi mong rằng câu trả lời là "có", nhưng simple check cho thấy rằng không phải như vậy.

constexpr 
bool f(int) noexcept 
{ 
    return true; 
} 

constexpr 
bool g(int) 
{ 
    return true; 
} 

static_assert(noexcept(f(1))); 
static_assert(noexcept(g(2))); // comment this line to check runtime behaviour 

#include <cassert> 
#include <cstdlib> 

int 
main(int argc, char * []) 
{ 
    assert(noexcept(f(argc))); 
    assert(noexcept(g(argc))); 
    return EXIT_SUCCESS; 
} 
+0

@ cad anyways t câu hỏi của anh ta rất chung chung, đừng nghĩ rằng có ví dụ cụ thể tốt. – Orient

+0

Ví dụ truy cập: 'constexpr void * foo (int n) {return n == 0? nullptr: toán tử new (n); } '. [Demo] (https://ideone.com/zlTsDI). –

+0

Tôi đã lạm dụng điều này một lần, xem http://stackoverflow.com/a/13305072/34509 –

Trả lời

13

không có điều này có thể không phải là trường hợp, bởi vì không phải mọi inovocation chức năng constexpr có để có thể được đánh giá là biểu hiện của một biểu thức liên tục lõi. Chúng tôi chỉ cần một giá trị đối số cho phép điều này. Vì vậy, một hàm constexpr có thể chứa một câu lệnh ném miễn là chúng ta có một giá trị đối số không gọi nhánh đó.

này được đề cập trong dự thảo C++ 14 phần chuẩn 7.1.5 Các constexpr specifier [dcl.constexpr] mà cho chúng ta biết những gì được cho phép trong một hàm constexpr:

Định nghĩa của một hàm constexpr phải đáp ứng các ràng buộc sau:

  • không được ảo (10.3);

  • loại trả về phải là loại chữ;

  • mỗi loại tham số của nó phải là một loại chữ;

  • nó chức năng thân phải = xóa, = mặc định, hoặc một hợp chất-tuyên bố rằng không chứa

    • một asm nét,

    • một lệnh goto,

    • thử khối, hoặc

    • một định nghĩa của một biến kiểu phi-đen hoặc tĩnh hoặc sợi st thời gian orage hoặc không khởi tạo được thực hiện.

mà như chúng ta có thể thấy không cấm throw và trong thực tế cấm rất ít kể từ đề nghị Relaxing constraints on constexpr functions trở thành một phần của C++ 14.

Dưới đây chúng ta thấy các quy tắc mà nói một hàm constexpr là tốt được hình thành nếu ít nhất một giá trị đối số tồn tại như vậy mà nó có thể được đánh giá là một subexpression của một biểu thức hằng lõi:

Đối với một phi mẫu, chức năng constexpr không được mặc định hoặc không thừa kế, không thừa kế, không kế thừa constructor constexpr, nếu không có giá trị đối số tồn tại sao cho yêu cầu hàm hoặc hàm tạo có thể là biểu thức con được đánh giá. (5.19), chương trình bị lỗi hình thành; không yêu cầu chẩn đoán .

và dưới đoạn này chúng ta có ví dụ sau, trong đó cho thấy một ví dụ hoàn hảo cho trường hợp này:

constexpr int f(bool b) 
    { return b ? throw 0 : 0; } // OK 
constexpr int f() { return f(true); } // ill-formed, no diagnostic required 

Vì vậy, chúng tôi mong chờ các đầu ra cho các ví dụ sau:

#include <iostream> 

constexpr int f(bool b) { return b ? throw 0 : 0; } 

int main() { 
    std::cout << noexcept(f(1)) << "\n" 
       << noexcept(f(0)) << "\n" ; 
} 

trở thành (see it live with gcc):

0 
1 

Visual Studio qua webcompiler cũng tạo ra kết quả tương tự. Như hvd lưu ý, clang có lỗi như được ghi trong báo cáo lỗi noexcept should check whether the expression is a constant expression.

Defect Báo cáo 1129

Defect report 1129: Default nothrow for constexpr functions hỏi cùng một câu hỏi:

Một chức năng constexpr không được phép trở lại thông qua một ngoại lệ.Điều này cần được công nhận, và một hàm được khai báo constexpr mà không có một đặc tả ngoại lệ rõ ràng nên được xử lý như thể khai báo noexcept (true) chứ không phải là thông thường noexcept (false). Đối với một mẫu hàm được khai báo constexpr không có một đặc tả ngoại lệ rõ ràng, nó phải được coi là noexcept (true) nếu và chỉ khi từ khóa constexpr được tôn trọng trên một instantiation đã cho.

và phản ứng là:

Những tiền đề là không đúng: một ngoại lệ bị cấm chỉ khi một chức năng constexpr được gọi trong một bối cảnh mà đòi hỏi một biểu thức hằng. Được sử dụng như một chức năng bình thường, nó có thể ném.

và nó biến đổi 5.3.7 [expr.unary.noexcept] đoạn 3 viên đạn 1 (Ngoài ra lưu ý với sự nhấn mạnh):

một call80 có khả năng đánh giá cho một hàm, hàm thành viên, chức năng con trỏ, hoặc thành viên con trỏ hàm mà không có một tổ chức phi ném ngoại lệ đặc điểm kỹ thuật (15,4 [except.spec]), trừ khi cuộc gọi là một biểu thức hằng số (5,20 [expr.const]),

5

It is said của noexcept rằng:

Kết quả là false nếu biểu thức chứa [...] gọi cho bất kỳ loại chức năng mà không có phi ném đặc tả ngoại lệ, trừ khi đó là một biểu thức liên tục.

Ngoài ra, khoảng constexpr, it is true rằng:

các nhà điều hành noexcept luôn trả về true cho một biểu thức hằng

Trong mọi trường hợp nào nó dường như ngụ ý rằng constexpr specifier buộc một noexcept chỉ định cho biểu thức được bao quanh, như một người nào đó đã hiển thị trong nhận xét bằng phản hồi và bạn cũng đã xác minh.

Dù sao, từ tài liệu, có các lưu ý thú vị sau đây về mối quan hệ giữa noexceptconstexpr:

Bởi vì các nhà điều hành noexcept luôn trả về true cho một biểu thức hằng số, nó có thể được sử dụng để kiểm tra xem một invocation cụ thể của một hàm constexpr mất chi nhánh biểu thức hằng

EDIT: ví dụ với GCC

Cảm ơn @hvd vì lời nhận xét/ví dụ thú vị của anh ấy với GCC về trích dẫn cuối cùng của tôi.

constexpr int f(int i) { 
    return i == 0 ? i : f(i - 1); 
} 

int main() { 
    noexcept(f(512)); 
    return noexcept(f(0)) == noexcept(f(0)); 
} 

Đoạn mã trên trả về 0, với một cảnh báo rằng tuyên bố noexcept(f(512)) không có tác dụng.
Nhận xét rằng tuyên bố được cho là không có hiệu lực, giá trị trả lại thay đổi thành 1.

EDIT: lỗi được biết đến trên kêu vang

Một lần nữa, nhờ @hvd cũng cho this liên kết, đó là về một lỗi nổi tiếng trong kêu vang về mã đề cập trong câu hỏi.

Trích dẫn từ báo cáo lỗi:

Quoth cuốn sách của C++, [expr.unary.noexcept] p3:

"Kết quả của toán tử noexcept là sai nếu trong ngữ cảnh có khả năng đánh giá biểu thức sẽ chứa cuộc gọi có khả năng được đánh giá đến hàm, hàm thành viên, con trỏ hàm hoặc con trỏ hàm thành viên không có một đặc tả ngoại lệ không ném (15.4), trừ khi cuộc gọi là một biểu thức không đổi (5.19) ".

Chúng tôi không triển khai cụm từ cuối cùng đó.

+1

Có lẽ một ví dụ thú vị với GCC của báo giá cuối cùng của bạn: 'constexpr int f (int i) {return i == 0? i: f (i - 1); } int main() {noexcept (f (512)); trả về noexcept (f (0)) == noexcept (f (0)); } '. Điều này trả về '0', với một cảnh báo rằng câu lệnh 'noexcept (f (512));' không có hiệu lực. Nhận xét ra câu lệnh được cho là không có hiệu lực, giá trị trả lại thay đổi thành '1'. – hvd

+0

@hvd Tôi đoán một ví dụ có thể được tìm thấy trong các tài liệu liên kết quá, dù sao cảm ơn bạn, nhận xét thú vị. – skypjack

+0

Đối với mã trong câu hỏi, [đã được biết đến như một lỗi trong clang] (https://llvm.org/bugs/show_bug.cgi?id=15481). Tôi thường đăng nó như là một câu trả lời riêng biệt, nhưng trong trường hợp này tôi nghĩ rằng nó đủ nhỏ để bạn có thể đưa nó vào câu trả lời của bạn, nếu bạn muốn. – hvd

2

Không, nói chung là không.

Một hàm constexpr có thể được gọi trong ngữ cảnh không constepr, trong đó nó được phép ném một ngoại lệ (ngoại trừ khóa học nếu bạn chỉ định nó theo cách thủ công là noexcept(true)).

Tuy nhiên, như một phần của biểu thức không đổi (ví dụ như trong ví dụ của bạn), nó sẽ hoạt động như được chỉ định là noexcept(true) (tất nhiên, nếu việc đánh giá biểu thức sẽ dẫn đến một ngoại lệ dẫn đến cuộc gọi đến std::terminate vì chương trình chưa chạy nhưng thay vào đó dẫn đến lỗi thời gian biên dịch).

Như tôi đã mong đợi, ví dụ của bạn không kích hoạt xác nhận tĩnh với MSVC và g ++. Tôi không chắc chắn, cho dù đây là một lỗi trong clang hoặc tôi là missunderstanding một cái gì đó trong tiêu chuẩn.

+0

Theo cách nào, nó có nghĩa là 'noexcept (true)', nếu bạn đã nói hiệu ứng của 'noexcept (true)' không áp dụng? – hvd

+0

@hvd: Khi skypjack trích dẫn: áp dụng toán tử noexcept với hàm constexpr trong ngữ cảnh constexpr (như trong câu hỏi mang lại giá trị true, giống như hàm được khai báo (noexcept (true)) – MikeMB

+0

Một hàm được khai báo 'noexcept (true) 'có hai hiệu ứng: một, nó làm cho các ngoại lệ chấm dứt chương trình. Hai, nó làm cho hàm gọi không ảnh hưởng đến kết quả của toán tử' noexcept'. Nó không có hiệu ứng đầu tiên. Nó hoạt động theo cách tương tự với hiệu ứng thứ hai, nhưng không chính xác theo cùng một cách, và một trong hai cách, hiệu ứng thứ hai không đạt được bằng cách ngầm tạo ra hàm 'noexcept (true) ', nó là do một ngoại lệ bổ sung trong các quy tắc của toán tử' noexcept', như được hiển thị trong câu trả lời của @ skypjack – hvd

2

Bạn được phép ném ngoại lệ trong một hàm constexpr. Nó được thiết kế như vậy để người thực hiện có thể chỉ ra lỗi cho trình biên dịch. Hãy xem xét rằng bạn có chức năng sau:

constexpr int fast_sqrt(int x) { 
    return (x < 0) ? throw invalid_domain() : fast_sqrt_impl(x); 
} 

Trong trường hợp này x là tiêu cực, chúng tôi cần dừng lập trình ngay lập tức và cho biết người dùng thông qua lỗi trình biên dịch. Điều này sau ý tưởng rằng các lỗi trình biên dịch là tốt hơn so với lỗi thời gian chạy (không nhanh). tiêu chuẩn

C++ nói này trong (5.20):

Một điều kiện thể hiện e là một biểu thức hằng lõi trừ khi việc đánh giá e, theo các quy tắc của máy trừu tượng (1.9), sẽ đánh giá một của các khái niệm sau:

- một ném-biểu (5,17)

+0

Tiêu chuẩn của bạn không hoàn toàn sao lưu câu trả lời của bạn, bạn cũng cần báo giá '7.1.5' như tôi làm trong [câu trả lời của tôi] (http://stackoverflow.com/a/34558600/1708801) để chứng minh đây là mã hợp lệ . –

+0

@Shafik, tôi đang tìm phần có tiêu chí cho hàm constexpr. Cảm ơn vì đã bao gồm điều đó! – Andrew

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