Có bao giờ là xấu khi đánh dấu một constexpr chức năng C ++?


26

Cho một chức năng rất tầm thường,

int transform(int val) {
    return (val + 7) / 8;
}

Một điều rất rõ ràng là thật dễ dàng để biến hàm này thành constexprhàm, cho phép tôi sử dụng nó khi xác định constexprcác biến, như vậy:

constexpr int transform(int val) {
    return (val + 7) / 8;
}

Giả định của tôi là đây hoàn toàn là một sự cải tiến, vì hàm vẫn có thể được gọi trong một constexprngữ cảnh và bây giờ nó cũng có thể được sử dụng để giúp xác định các biến hằng thời gian biên dịch.

Câu hỏi của tôi là, có những tình huống mà đây là một ý tưởng tồi? Giống như, bằng cách thực hiện chức năng này constexpr, tôi có thể gặp phải một tình huống mà chức năng này sẽ không còn có thể sử dụng được trong một trường hợp cụ thể, hoặc nơi nó sẽ hoạt động sai?


1
Điều duy nhất tôi có thể nghĩ là lỗi trình biên dịch. Có thể một lệnh gọi hàm constexpr đệ quy có thể gây ra một bước biên dịch thực sự chậm hoặc thậm chí là một trình biên dịch thoát khỏi sự cố bộ nhớ.
Zan Lynx

Câu trả lời:


19

Điều này chỉ quan trọng nếu chức năng là một phần của giao diện công cộng và bạn muốn giữ các phiên bản tương lai của API tương thích nhị phân. Trong trường hợp đó, bạn phải suy nghĩ cẩn thận về cách bạn muốn phát triển API của mình và nơi bạn cần các điểm mở rộng cho các thay đổi trong tương lai.

Điều đó làm cho một constexprvòng loại là một quyết định thiết kế không thể hủy bỏ. Bạn không thể xóa vòng loại này mà không có thay đổi không tương thích với API của bạn. Nó cũng giới hạn cách bạn có thể thực hiện chức năng đó, ví dụ: bạn sẽ không thể thực hiện bất kỳ đăng nhập nào trong chức năng này. Không phải mọi chức năng tầm thường sẽ ở lại tầm thường trong cõi vĩnh hằng.

Điều đó có nghĩa là tốt nhất bạn nên sử dụng constexprcho các hàm vốn là các hàm thuần túy và điều đó thực sự hữu ích trong thời gian biên dịch (ví dụ: đối với siêu lập trình mẫu). Sẽ không tốt khi tạo các hàm constexpr chỉ vì việc triển khai hiện tại có thể là constexpr.

Trong trường hợp không cần đánh giá thời gian biên dịch, sử dụng các hàm hoặc hàm nội tuyến có liên kết nội bộ có vẻ phù hợp hơn constexpr. Tất cả các biến thể này đều có điểm chung là phần thân chức năng là công khai và có sẵn trong cùng một đơn vị biên dịch với vị trí cuộc gọi.

Nếu chức năng được đề cập không phải là một phần của API công khai, ổn định, thì đây không phải là vấn đề vì bạn có thể tùy ý thay đổi thiết kế theo ý muốn. Nhưng vì bây giờ bạn kiểm soát tất cả các trang web cuộc gọi, không cần thiết phải đánh dấu một hàm constexpr chỉ trong trường hợp. Bạn biết liệu bạn có đang sử dụng chức năng này trong ngữ cảnh constexpr hay không. Thêm các vòng loại hạn chế không cần thiết sau đó có thể được coi là obfuscation.


12

Đánh dấu một chức năng constexprcũng làm cho nó trở thành một hàm nội tuyến § [dcl.constexpr] / 1:

Hàm hoặc thành viên dữ liệu tĩnh được khai báo với bộ xác định constexpr hoàn toàn là hàm hoặc biến nội tuyến (7.1.6).

inlinelần lượt, có nghĩa là bạn cần bao gồm định nghĩa của hàm đó trong mọi đơn vị dịch mà nó có thể được sử dụng. Điều đó về cơ bản có nghĩa là các constexprchức năng phải là:

  1. bị hạn chế sử dụng trong một đơn vị dịch hoặc
  2. được định nghĩa trong một tiêu đề.

Hầu hết các hàm điển hình mà bạn muốn khai báo trong một tiêu đề và xác định trong một tệp nguồn (và bất kỳ thứ gì khác sử dụng chúng chỉ bao gồm tiêu đề, sau đó liên kết với tệp đối tượng của nguồn đó) constexprsẽ không hoạt động.

Về lý thuyết, tôi cho rằng bạn chỉ có thể chuyển mọi thứ vào các tiêu đề và chỉ có một tệp nguồn chỉ bao gồm tất cả các tiêu đề, nhưng điều này sẽ làm tổn thương thời gian biên dịch một cách quyết liệt và đối với hầu hết các dự án nghiêm túc sẽ cần rất nhiều bộ nhớ để biên dịch.

Một constexprchức năng cũng bị hạn chế theo một số cách, vì vậy đối với một số chức năng, nó có thể không phải là một tùy chọn. Các hạn chế bao gồm:

  1. chức năng ảo không thể constexpr.
  2. kiểu trả về của nó phải là "kiểu chữ" (ví dụ: không có đối tượng nào có bộ đệm hoặc bộ ba không phải là bộ ba).
  3. tất cả các tham số của nó phải là loại chữ.
  4. cơ thể chức năng không thể chứa một trykhối.
  5. nó không thể chứa định nghĩa biến của loại không theo nghĩa đen hoặc bất cứ thứ gì có thời lượng lưu trữ tĩnh hoặc luồng.

Tôi đã bỏ qua một vài điều khá mơ hồ (ví dụ, nó cũng không thể chứa một gotohoặc một asmtuyên bố), nhưng bạn có ý tưởng - đối với một vài điều, nó sẽ không hoạt động.

Điểm mấu chốt: có, có khá nhiều tình huống mà đây sẽ là một ý tưởng tồi.


"Nó không phải là ảo (cho đến C ++ 20)" Tôi đang tự hỏi làm thế nào một chức năng ảo có thể là constexpr? Trình biên dịch làm gì?
hỗn loạn
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.