2016-02-08 13 views
5

Gần đây tôi đã đặt câu hỏi tại đây (Detecting instance method constexpr with SFINAE), nơi tôi đã cố gắng thực hiện một số phát hiện constexpr tại thời gian biên dịch. Cuối cùng, tôi đã tìm ra rằng người ta có thể khai thác noexcept để làm điều này: bất kỳ biểu thức liên tục nào cũng là noexcept. Vì vậy, tôi cùng nhau đưa các máy móc thiết bị sau:Constexpr decltype

template <class T> 
constexpr int maybe_noexcept(T && t) { return 0; } 
... 
constexpr bool b = noexcept(maybe_noexcept(int{})); 

này hoạt động và b là đúng như bạn mong muốn, như zero-khởi tạo một int là một biểu thức hằng. Nó cũng tạo ra số không chính xác khi cần (nếu tôi thay đổi int thành một số loại thích hợp khác).

Tiếp theo, tôi muốn kiểm tra xem có điều gì đó là constexpr di chuyển có thể cấu hình hay không. Vì vậy, tôi đã làm điều này:

constexpr bool b = noexcept(maybe_noexcept(int(int{}))); 

Và một lần nữa, điều này hoạt động đúng cho int hoặc loại do người dùng xác định. Tuy nhiên, điều này kiểm tra xem loại có cả hàm tạo mặc định constexpr và hàm khởi tạo constexpr hay không. Vì vậy, để làm việc xung quanh này, tôi đã cố gắng để thay đổi declval:

constexpr bool b = noexcept(maybe_noexcept(int(declval<int>()))); 

Điều này dẫn đến b là sai trong gcc 5.3.0 (không thể sử dụng kêu vang cho bất kỳ này, bởi vì kêu vang không chính xác làm cho liên tục biểu thức noexcept). Không có vấn đề, tôi nói, phải là vì declval là (đủ thú vị) không được đánh dấu constexpr. Vì vậy, tôi viết phiên bản ngây thơ của riêng tôi:

template <class T> 
constexpr T&& constexpr_declval() noexcept; 

Có, đây là ngây thơ so với cách các thư viện chuẩn thực hiện nó vì nó sẽ nghẹt thở trên khoảng trống và có lẽ những thứ khác, nhưng nó tốt cho bây giờ. Vì vậy, tôi thử lại:

constexpr bool b = noexcept(maybe_noexcept(int(constexpr_declval<int>()))); 

Điều này vẫn không hoạt động, b luôn luôn là sai. Tại sao điều này không được coi là một biểu thức liên tục? Đây có phải là lỗi trình biên dịch hay tôi không hiểu cơ bản về constexpr? Có vẻ như có một số tương tác lạ giữa constexpr và ngữ cảnh không được đánh giá.

+1

@Cameron Phần thứ hai của những gì bạn nói chắc chắn là đúng, nhưng về mặt kỹ thuật đầu tiên thì không. Mỗi biểu thức liên tục * là * '' noexcept'', nó không phải là nó có thể. Tuy nhiên, sự trở lại của một hàm '' constexpr'' không phải lúc nào cũng là một biểu thức liên tục. –

+1

Lệnh gọi hàm của bạn không phải là biểu thức liên tục vì nó không được định nghĩa ([expr.const] /2.3) – 0x499602D2

Trả lời

6

constexpr biểu thức phải được xác định. Bạn không được xác định, vì vậy trong trường hợp đó int(constexpr_declval<int>()) không phải là constexpr.

Có nghĩa là maybe_noexcept(int(constexpr_declval<int>())) không phải là constexpr, vì vậy không phải là noexcept.

Và trình biên dịch trả về đúng cách false.

Bạn cũng không thể gọi UB theo số constexpr.

Tôi không thể nghĩ ra cách để thực hiện tham chiếu constexpr cho dữ liệu tùy ý. Tôi đã suy nghĩ một bộ đệm constexpr của lưu trữ liên kết được diễn giải lại như là một tham chiếu đến kiểu dữ liệu, nhưng đó là UB trong nhiều ngữ cảnh, do đó không phải là constexpr.

Nói chung, điều này là không thể. Hãy tưởng tượng bạn có một lớp học mà nhà nước xác định nếu phương thức gọi là constexpr:

struct bob { 
    int alice; 
    constexpr bob(int a=0):alice(a) {} 
    constexpr int get() const { 
    if (alice > 0) throw std::string("nope"); 
    return alice; 
    } 
}; 

bây giờ, là bob::getconstexpr hay không? Nếu bạn có một constexpr bob được tạo với một số không tích cực alice và ...nó không phải là không.

Bạn không thể nói "giả vờ giá trị này là constexpr và cho tôi biết nếu một số biểu thức là constexpr". Ngay cả khi bạn có thể, nó sẽ không giải quyết được vấn đề nói chung, vì trạng thái của thông số constexpr có thể thay đổi nếu một biểu thức là constexpr hay không!

Thú vị hơn nữa, bob().get()là constexpr, trong khi bob(1).get() thì không. Vì vậy, nỗ lực đầu tiên của bạn (mặc định xây dựng kiểu) thậm chí đã đưa ra câu trả lời sai: bạn có thể kiểm tra, sau đó thực hiện hành động và hành động sẽ thất bại.

Đối tượng có hiệu quả là tham số cho phương thức và không có trạng thái của các tham số al, bạn không thể xác định xem hàm có là constexpr hay không.

Cách xác định biểu thức là constexpr là chạy nó trong ngữ cảnh constexpr và xem nó có hoạt động hay không.

+0

Vì vậy, có vẻ như không thể kiểm tra đúng nếu phương thức instance constexpr tồn tại cho một lớp, vì bạn cần có ví dụ để làm điều đó, và không có cách nào để có được một cá thể constexpr vừa được định nghĩa thực sự và không giả sử sự tồn tại của một số hàm tạo. Đây có phải là mô tả công bằng về tình huống không? –

+1

@NirFriedman Vâng, bạn có thể vượt qua các đối số để xây dựng đối tượng đã nói. ;) Nói chung, 'constexpr' phụ thuộc vào ngữ cảnh - bạn cần phải biết gần như mọi thứ * về cuộc gọi và các tham số của nó. Một trong các tham số là đối tượng * thực hiện cuộc gọi *: nếu nó không phải là 'constexpr' thì biểu thức là (nói chung) thì không. Nói "nếu giả vờ đối số này là' constexpr' mặc dù nó không phải là những gì xảy ra "không được hỗ trợ, theo như tôi biết. – Yakk

+0

Thực sự là Haha. Được rồi, câu trả lời của bạn + nhận xét rất thông tin, cảm ơn bạn. –

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