2016-10-27 18 views
5

(Sử dụng g++ 7.0 cốp xe.)đánh giá không phù hợp cho `lambdas constexpr` trong các mẫu giữa 'static_assert`,` nếu constexpr (...) `và` biến constexpr`

Do sau "loại-to -giá trị gói" tiện ích ...

template <typename T> 
struct type_wrapper { using type = T; }; 

// "Wraps" a type into a `constexpr` value. 
template <typename T> 
constexpr type_wrapper<T> type_c{}; 

... tôi tạo ra các chức năng sau đây để kiểm tra tính hợp lệ của một biểu thức:

template <typename TF> 
constexpr auto is_valid(TF) 
{ 
    return [](auto... ts) constexpr 
    { 
     return std::is_callable<TF(typename decltype(ts)::type...)>{}; 
    }; 
} 

Chức năng is_valid có thể được sử dụng như sau:

// Evaluates to `true` if `some_A.hello()` is a valid expression. 
constexpr auto can_add_int_and_float = 
    is_valid([](auto _0) constexpr -> decltype(_0.hello()){}) 
     (type_c<A>); 

// Evaluates to `true` if `some_int + some_float` is a valid expression. 
constexpr auto can_add_int_and_float = 
    is_valid([](auto _0, auto _1) constexpr -> decltype(_0 + _1){}) 
     (type_c<int>, type_c<float>); 

Nó cũng có thể được sử dụng bên static_assert ...

static_assert(is_valid([](auto _0) constexpr -> decltype(_0.hello()){}) 
     (type_c<A>)); 

... và bên trong if constexpr:

if constexpr(
    is_valid([](auto _0) constexpr -> decltype(_0.hello()){}) 
     (type_c<A>)) { /* ... */ } 

Tuy nhiên, khi is_valid được sử dụng bên trong một mẫu chức năng (đi qua các thông số mẫu như type_c giá trị), một cái gì đó kỳ lạ xảy ra:

  • static_assert(is_valid(/*...*/)) công trình đúng cách.

  • constexpr auto x = is_valid(/*...*/) hoạt động bình thường.

  • if constexpr(is_valid(/*...*/)không thể biên dịch.

// Compiles and works as intended. 
template <typename T0, typename T1> 
void sum_ok_0(T0, T1) 
{ 
    static_assert(
     is_valid([](auto _0, auto _1) constexpr 
       -> decltype(_0 + _1){})(type_c<T0>, type_c<T1>) 
    ); 
} 

// Compiles and works as intended. 
template <typename T0, typename T1> 
void sum_ok_1(T0, T1) 
{ 
    constexpr auto can_sum = 
     is_valid([](auto _0, auto _1) constexpr 
       -> decltype(_0 + _1){})(type_c<T0>, type_c<T1>); 

    if constexpr(can_sum) { } 
} 

// Compile-time error! 
template <typename T0, typename T1> 
void sum_fail_0(T0, T1) 
{ 
    if constexpr(is_valid([](auto _0, auto _1) constexpr 
     -> decltype(_0 + _1){})(type_c<T0>, type_c<T1>)) { } 
} 

Lỗi:

In function 'void sum_fail_0(T0, T1)': 
64:95: error: expression '<lambda>' is not a constant expression 
    if constexpr(is_valid([](auto _0, auto _1) constexpr -> decltype(_0 + _1){})(type_c<T0>, type_c<T1>)) { } 

Tại sao điều này thất bại trong việc biên dịch chỉ dành cho các trường hợp if constexpr(is_valid(/*...*/))? Điều này không phù hợp với static_assertconstexpr auto x = /*...*/.

Đây có phải là lỗi trong việc triển khai if constexpr không?

Full example on wandbox.

+2

Bạn đã phát minh lại [std :: is_callable] (http://en.cppreference.com/w/cpp/types/is_callable). –

+2

@SamVarshavchik: không còn nữa :) –

+4

Chắc chắn trông giống như một lỗi. –

Trả lời

1

Các hành vi không phù hợp đã được báo cáo là bug #78131.

0

Từ những gì tôi có thể thu thập, bản thân lambda không phải là constexpr. Bạn có thể làm

constexpr auto f = [](auto _0, auto _1) -> decltype(_0 + _1){}; 

Và sau đó

if constexpr(is_valid(f)(type_c<T0>, type_c<T1>)) { } 
+0

Ngay cả khi nói rõ ràng 'is_valid ([] (tự động _0, tự động _1) constexpr -> decltype (_0 + _1) {}' không thay đổi kết quả. Tại sao đặt chính lambda trong biến 'constexpr' và trong' static_assert' làm việc nhưng không phải trong 'if constexpr'? –

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