2017-12-07 20 views
7

Các mã sau đây được chấp nhận bởi GCC 7.2 và kêu vang 5.0.0, nhưng bị từ chối bởi Microsoft VS 2017 15.5.0 Preview 5 và Intel C++ 19:lambda Generic và đối số của nó là biểu thức hằng

struct S { }; 

constexpr int f(S) 
{ 
    return 0; 
} 

int main() 
{ 
    auto lambda = [](auto x) 
    { 
     constexpr int e = f(x); 
    }; 

    lambda(S{}); 
} 

Microsoft:

<source>(12): error C2131: expression did not evaluate to a constant 

Intel:

<source>(12): error: expression must have a constant value 
    constexpr int e = f(x); 
        ^
<source>(12): note: the value of parameter "x" (declared at line 10) cannot be used as a constant 
    constexpr int e = f(x); 
         ^

Nếu tôi thay f(x) với f(decltype(x){}), cả Microsoft và Intel đều không phàn nàn. Tôi hiểu rằng x không phải là một biểu thức liên tục, nhưng nó không được sử dụng bên trong f. Đây có lẽ là lý do tại sao GCC và clang không phàn nàn.

Tôi đoán rằng trình biên dịch Microsoft và Intel là chính xác khi từ chối mã này. Bạn nghĩ sao?

+0

@RichardHodges, '-std = C++ 14'. – Evgeny

+0

OK bây giờ tôi nghĩ rằng đây là một lỗi của gcc và clang như C++ 14 không đi kèm với lambda constexpr ... này có sẵn từ C++ 17 –

+0

@WF, lambda là cố ý không 'constexpr' (trong mã thực nó không phải là). – Evgeny

Trả lời

4

Từ [expr.const]:

Một biểu hiện e là một lõi liên tục biểu trừ khi việc đánh giá e, theo các quy tắc của máy trừu tượng, sẽ đánh giá một trong những biểu hiện sau:

  • [...]
  • một chuyển đổi giá trị trái-to-rvalue trừ khi nó được áp dụng cho

    • một glvalue non-volatile của thiếu hoặc liệt kê kiểu đó đề cập đến một đối tượng const non-volatile hoàn chỉnh với một khởi trước, khởi tạo với một biểu thức không đổi, hoặc
    • một glvalue không bay hơi đề cập đến một phần con của một chuỗi ký tự, hoặc
    • một glvalue không bay hơi đề cập đến một đối tượng không bay hơi được xác định bằng constexpr, hoặc đề cập đến một -mục phụ có thể biến đổi của một đối tượng như vậy, hoặc
    • glvalue không bay hơi của loại chữ ám chỉ đến một đối tượng không bay hơi có tuổi thọ bắt đầu trong quá trình đánh giá e;
  • [...]

Trong f(x), chúng tôi làm một chuyển đổi giá trị trái-to-rvalue trên x. x không phải là kiểu tích phân hoặc kiểu liệt kê, nó không phải là một đối tượng con của một chuỗi ký tự, nó không phải là một đối tượng được định nghĩa với constexpr, và tuổi thọ của nó không bắt đầu với việc đánh giá f(x).

Điều đó dường như làm cho điều này không phải là biểu thức liên tục cốt lõi.

Tuy nhiên, như Casey chỉ ra, vì S trống, không có gì trong trình tạo bản sao ngầm được tạo ra của nó thực sự kích hoạt chuyển đổi từ rvalue này sang giá trị khác. Điều đó có nghĩa là không có gì trong biểu thức này thực sự vi phạm bất kỳ hạn chế biểu thức liên tục cốt lõi nào, và do đó gcc và clang là chính xác khi chấp nhận nó. Cách giải thích này có vẻ đúng với tôi. constexpr thật thú vị.

+2

"Trong' f (x) ', chúng tôi thực hiện chuyển đổi từ rvalue sang rvalue trên' x' "Tôi nghĩ điều này là sai. Vì 'S' rỗng, nên hàm tạo bản sao của nó không bao giờ thực hiện chuyển đổi lvalue-to-rvalue trên lvalue nguồn. Một bản sao của một biểu thức không liên tục 'S' vẫn có thể là một biểu thức không đổi (https://godbolt.org/g/m4jSgz). 'constexpr' là điên đôi khi. – Casey

+0

@Casey Tôi nghĩ đó thực sự là giải thích đúng. Điên, thật vậy. – Barry

2

Đây không phải là lỗi gcc/clang. Các hành vi tương tự có thể được sao chép trong C++ 11 với một mẫu chức năng:

template <typename T> 
void foo(T x) 
{ 
    constexpr int e = f(x); 
} 

int main() 
{ 
    foo(S{}); 
} 

on godbolt.org


Câu hỏi đặt ra là, cho ...

template <typename T> 
void foo(T x) 
{ 
    constexpr int e = f(x); 
} 

... là f(x) một biểu thức liên tục?

Từ [expr.const]:

Một biểu e là một lõi liên tục biểu trừ khi việc thẩm định e, theo các quy tắc của máy trừu tượng, sẽ đánh giá một trong những biểu hiện sau:

  • gọi hàm của một hàm khác không phải là một hàm tạo constexpr cho một lớp theo nghĩa đen, hàm constexpr hoặc yêu cầu tiềm ẩn của một destructor tầm thường

S{}0 là những biểu hiện thường xuyên bởi vì nó không vi phạm bất kỳ quy tắc trong [expr.const]. f(x) là một biểu thức liên tục vì nó là một lời gọi hàm constexpr.

Trừ khi tôi thiếu thứ gì đó, gcc và clang là chính xác ở đây.

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