constexpr 的等效三元运算符 if?

Equivalent ternary operator for constexpr if?

也许我遗漏了什么,但我找不到任何提示:C++17 中是否有等同于 constexpr-if 的 constexpr 三元运算符?

template<typename Mode>
class BusAddress {
public:
    explicit constexpr BusAddress(Address device) : 
        mAddress(Mode::write ? (device.mDevice << 1) : (device.mDevice << 1) | 0x01) {}
private:
    uint8_t mAddress = 0;    
};

不,没有 constexepr 条件运算符。但是你可以将整个东西包装在一个 lambda 中并立即对其进行评估(IIFE):

template<typename Mode>
class BusAddress {
public:
    explicit constexpr BusAddress(Address device)
     : mAddress([&]{
          if constexpr (Mode::write) {
            return device.mDevice << 1;
          }
          else {
            return (device.mDevice << 1) | 0x01;
          }         
        }())
     { }
private:
    uint8_t mAddress = 0;    
};

它可能不是有史以来最性感的代码,但它完成了工作。请注意,自 N4487 and P0170.

起,lambda 表达式默认为 constexpr

您似乎认为 if constexpr 是一种性能优化。 不是。如果将常量表达式放在 ?: 子句中,任何值得使用的编译器都会找出它解析的内容并删除条件。因此,对于特定的 Mode.

,您编写的代码几乎肯定会编译成单个选项

if constexpr的主要目的是完全消除另一个分支。也就是说,编译器甚至不检查它在语法上是否有效。这将适用于你 if constexpr(is_default_constructible_v<T>) 的事情,如果它是真的,你就会 T()。对于常规 if 语句,如果 T 不是默认可构造的,即使周围的 if 子句是常量表达式,T() 仍然必须是语法上有效的代码。 if constexpr 删除该要求;编译器将丢弃不在其他条件下的语句。

对于 ?:,这变得更加复杂,因为表达式的类型基于两个值的类型。因此,两个表达式都必须是合法的表达式,即使其中一个从未被求值。 ?:constexpr 形式可能会丢弃未在编译时采用的替代方案。因此表达式的类型实际上应该只基于其中之一。

那是一种非常不同的东西。

为方便起见,也可以将已接受的答案翻译成模板函数:

#include <type_traits>
#include <utility>

template <bool cond_v, typename Then, typename OrElse>
decltype(auto) constexpr_if(Then&& then, OrElse&& or_else) {
    if constexpr (cond_v) {
        return std::forward<Then>(then);
    } else {
        return std::forward<OrElse>(or_else);
    }
}

// examples

struct ModeFalse { static constexpr bool write = false; };
struct ModeTrue { static constexpr bool write = true; };

struct A {};
struct B {};

template <typename Mode>
auto&& test = constexpr_if<Mode::write>(A{}, B{});

static_assert(std::is_same_v<A&&, decltype(test<ModeTrue>)>);
static_assert(std::is_same_v<B&&, decltype(test<ModeFalse>)>);

const A a;
B b;

template <typename Mode>
auto&& test2 = constexpr_if<Mode::write>(a, b);

static_assert(std::is_same_v<const A&, decltype(test2<ModeTrue>)>);
static_assert(std::is_same_v<B&, decltype(test2<ModeFalse>)>);