如何在constexpr函数中强制编译错误,而不是让它衰变为非constexpr上下文?

时间:2017-04-12 17:03:57

标签: c++ c++11 constexpr static-assert

这是我希望能够编写的代码:

int id1 = not_const_expr_1();
int id10 = not_const_expr_10();

constexpr Device& cD1 = get_device(1);    // 1. ok
constexpr Device& cD10 = get_device(10);  // 2. compile error

Device& cD1 = get_device(1);    // 3. ok
Device& cD10 = get_device(10);  // 4. compile error

Device& D1 = get_device(id1);    // 5. ok
Device& D10 = get_device(id10);  // 6. exception, log message, infinite loop, or something

这是我尝试的内容:

template<typename T>
T failed(const char*) { while(1); }  // could also throw exception

constexpr Device& dev1 = ...;
constexpr Device& dev42 = ...;

constexpr Device& get_device(uint8_t which) {
    return which == 1 ? dev1 :
           which == 42 ? dev42 :
           // more here, but not important
           failed<Device&>("Device does not exist");
           // string argument just to aid with compiler error message
}

这传递了除了数字4之外的所有上述测试用例。不幸的是,错误没有在编译时被捕获,因为编译器发现对于给定的参数,get_device不是const_expr

有没有什么方法可以在这里工作static_assert,而不会破坏非constexpr上下文中的内容?

1 个答案:

答案 0 :(得分:2)

这是一个接近的黑客:

#define _return_constexpr(x) static_assert(((void)x, true), ""); return x
template <uint8_t which>
constexpr Device& get_device() {
    _return_constexpr(get_device(which));
}

将测试通过:

constexpr Device& cD1 = get_device(1);      // 1a. ok
constexpr Device& cD1 = get_device<1>();    // 1b. ok
constexpr Device& cD10 = get_device(10);    // 2a. compile error
constexpr Device& cD10 = get_device<10>();  // 2b. compile error

Device& cD1 = get_device(1);      // 3a. ok
Device& cD1 = get_device<1>();    // 3b. ok
Device& cD10 = get_device(10);    // 4a. runtime error (FAIL)
Device& cD10 = get_device<10>();  // 4b. compile error (PASS!)

Device& D1 = get_device(id1);    // 5. ok
Device& D10 = get_device(id10);  // 6. ok

似乎static_assert是迫使constexpr评估的独特背景:

  • 适用于任何类型(模板参数不接受Device&
  • 在constexpr上下文中工作(数组大小需要声明)

是否有更简洁的方法来编写此宏?

如果没有要求结果本身在constexpr上下文中工作,我们可以放弃宏:

template <uint8_t which>
Device& get_device() {
    constexpr auto& ret = get_device(which);
    return ret;
}