Constexpr có ngụ ý nội tuyến không?


105

Hãy xem xét hàm nội tuyến sau:

// Inline specifier version
#include<iostream>
#include<cstdlib>

inline int f(const int x);

inline int f(const int x)
{
    return 2*x;
}

int main(int argc, char* argv[])
{
    return f(std::atoi(argv[1]));
}

và phiên bản tương đương của constexpr:

// Constexpr specifier version
#include<iostream>
#include<cstdlib>

constexpr int f(const int x);

constexpr int f(const int x)
{
    return 2*x;
}

int main(int argc, char* argv[])
{
    return f(std::atoi(argv[1]));
}

Câu hỏi của tôi là: liệu trình constexprxác định có ngụ ý trình inlinexác định theo nghĩa là nếu một đối số không phải là hằng số được truyền cho một constexprhàm, trình biên dịch sẽ cố gắng thực inlinehiện hàm như thể trình inlinexác định được đưa vào khai báo của nó không?

Tiêu chuẩn C ++ 11 có đảm bảo điều đó không?


5
'[Sẽ] trình biên dịch cố gắng nội dòng hàm' không phải là những gì trình inlinexác định làm. (Hoặc có thể tôi đã hiểu sai cách diễn đạt của bạn.)
Luc Danton

5
Công inlinecụ xác định không còn liên quan gì đến nội tuyến nữa
K-ball

2
Câu hỏi đặt ra giả định sai inlinecó liên quan trực tiếp đến nội tuyến. Vì vậy, không, các constexprspecifier không bao hàm các inlinespecifier trong ý nghĩa đó, như cảm giác không tồn tại.
Christian Rau

Câu trả lời:


139

Có ([dcl.constexpr], §7.1.5 / 2 trong tiêu chuẩn C ++ 11): "Các hàm constexpr và các hàm tạo constexpr là nội tuyến hoàn toàn (7.1.2)."

Tuy nhiên, lưu ý rằng trình inlinexác định thực sự có rất ít (nếu có) ảnh hưởng đến việc trình biên dịch có khả năng mở rộng một hàm nội tuyến hay không. Tuy nhiên, nó ảnh hưởng đến một quy tắc định nghĩa và từ quan điểm đó, trình biên dịch được yêu cầu tuân theo các quy tắc tương tự cho một constexprhàm như một inlinehàm.

Tôi cũng nên nói thêm rằng bất kể constexprngụ ý inline, các quy tắc cho các constexprhàm trong C ++ 11 yêu cầu chúng phải đủ đơn giản để chúng thường là ứng cử viên tốt để mở rộng nội tuyến (ngoại lệ chính là những hàm đệ quy). Tuy nhiên, kể từ đó, các quy tắc ngày càng lỏng lẻo hơn, do đó constexprcó thể được áp dụng cho các hàm lớn hơn, phức tạp hơn về cơ bản.


Cho rằng ý tưởng là hằng số biểu thức được đánh giá tại thời gian biên dịch, tôi cho rằng hầu hết cách dùng constexprchức năng sẽ không gây ra bất kỳ thế hệ mã tại tất cả các ...
Kerrek SB

11
Các constexprhàm @KerrekSB có khả năng được đánh giá tại thời điểm biên dịch. Tuy nhiên, tiêu chuẩn C ++ 14 có rất nhiều tiêu chuẩn sẽ được gọi trong thời gian chạy. Ví dụ:std::array<T,N>::at
Eponymous,

@Eponymous có nhưng chỉ dạng giảm nhất sẽ vẫn là opcodes. ví dụ: các kiểm tra ràng buộc, sẽ được đánh giá tại thời điểm xây dựng, vì đường dẫn mã của chúng là const. Nhưng giá trị trả về sẽ là * (data + offset)
v.oddou

16

constexprkhông ngụ ý inlinecho các biến không tĩnh (C ++ 17 biến nội tuyến)

Mặc dù constexprngụ ý inlineđối với các hàm, nhưng nó không có tác dụng đó đối với các biến không tĩnh, khi xem xét các biến nội tuyến trong C ++ 17.

Ví dụ, nếu bạn lấy ví dụ tối thiểu mà tôi đã đăng tại: Các biến nội tuyến hoạt động như thế nào? và loại bỏ inline, chỉ để lại constexpr, khi đó biến nhận được nhiều địa chỉ, đây là điều chính mà các biến nội tuyến tránh.

constexpr Tuy nhiên, các biến static là tĩnh hoàn toàn.

Ví dụ tối thiểu constexprngụ ý inlinecho các hàm

Như đã đề cập tại: https://stackoverflow.com/a/14391320/895245 , tác dụng chính của inlinenó không phải là nội dòng mà là cho phép nhiều định nghĩa của một hàm, trích dẫn tiêu chuẩn tại: Làm thế nào để một tệp tiêu đề C ++ có thể bao gồm triển khai?

Chúng ta có thể quan sát điều đó bằng cách chơi với ví dụ sau:

main.cpp

#include <cassert>

#include "notmain.hpp"

int main() {
    assert(shared_func() == notmain_func());
}

notmain.hpp

#ifndef NOTMAIN_HPP
#define NOTMAIN_HPP

inline int shared_func() { return 42; }
int notmain_func();

#endif

notmain.cpp

#include "notmain.hpp"

int notmain_func() {
    return shared_func();
}

Biên dịch và chạy:

g++ -c -ggdb3  -O0 -Wall -Wextra -std=c++11 -pedantic-errors  -o 'notmain.o' 'notmain.cpp' 
g++ -c -ggdb3  -O0 -Wall -Wextra -std=c++11 -pedantic-errors  -o 'main.o' 'main.cpp' 
g++ -ggdb3  -O0 -Wall -Wextra -std=c++11 -pedantic-errors  -o 'main.out' notmain.o main.o
./main.out

Nếu chúng tôi xóa inlinekhỏi shared_func, liên kết sẽ không thành công với:

multiple definition of `shared_func()'

vì tiêu đề được đưa vào nhiều .cpptệp.

Nhưng nếu chúng ta thay thế inlinebằng constexpr, sau đó nó hoạt động trở lại, bởi vì constexprcũng ngụ ý inline.

GCC thực hiện điều đó bằng cách đánh dấu các ký hiệu là yếu trên tệp đối tượng ELF: Làm cách nào để tệp tiêu đề C ++ có thể bao gồm việc triển khai?

Đã thử nghiệm trong GCC 8.3.0.


3
BTW, một biến thành viên lớp tĩnh được khai báo constexprvẫn nằm trong dòng. cppreference.com : Biến thành viên tĩnh (nhưng không phải là biến phạm vi không gian tên) được khai báo constexprngầm định là một biến nội tuyến.
anton_rh

@anton_rh cảm ơn, tôi chưa thấy quy tắc đó, hãy cập nhật câu trả lời.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

nó không phải là những gì open-std.org/JTC1/SC22/WG21/docs/papers/2016/p0386r0.pdf nói. nó nói rằng constexpr ngụ ý nội tuyến cho các biến. không đề cập đến sự khác biệt giữa phạm vi không gian tên của phạm vi lớp.
v.oddou
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.