【问题标题】:Equivalent ternary operator for constexpr if?constexpr if 的等效三元运算符?
【发布时间】:2016-12-07 07:44:45
【问题描述】:

也许我遗漏了一些东西,但我找不到任何提示: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;    
};

【问题讨论】:

  • 不,没有。但是,如果您告诉我们更多您想要做什么,我们可以建议一个解决方法。
  • 假设如果三元表达式可以是constexpr,那么编译器将在编译时计算它是否可以? (换句话说,不需要特殊的constexpr 三元运算符)
  • @qxz 我认为与if constexpr 的真正类似物还具有未采用的分支将被丢弃的属性(因此即使该分支未编译,整个事情也会编译)。
  • 刚刚加了一个小例子
  • 解决方法:将您的三元 if 包装在 constexpr 函数中。然后调用它来初始化类成员。

标签: c++ constexpr c++17 if-constexpr


【解决方案1】:

不,没有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;    
};

它可能不是有史以来最性感的代码,但它可以完成工作。请注意,从 N4487P0170 开始,lambda 默认为 constexpr

【讨论】:

    【解决方案2】:

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

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

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

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

    【讨论】:

    • 我正在寻找这样一个 constexpr ?: 正是因为类型差异:我想用表达式的结果移动构造一个声明为 auto 的局部变量,我可以' t 使用常规 if constexpr 而不将其包装在函数中(或 IIFE,请参阅 @Barry 的答案)。我宁愿避免那种句法噪音。
    • 即使被丢弃的语句也必须语法有效,但它们不必在语义上有效(并且它们可以做准语法的事情,例如声明不存在的嵌套类型的变量)。
    • 编译器会根据 constexpr 更改调试(非优化)构建中的代码生成。因此,虽然它不是发布版本的优化,但它非常可以是调试版本的优化,以在条件中使用 constexpr。这在存在许多代码路径但根据模板参数只有一个可能的复杂模板中特别有用。过去,此类函数在尝试调试问题时非常繁重,但使用 constexpr 可以大大减轻负担,而无需依赖编译器的巧妙优化功能和调试符号信息。
    【解决方案3】:

    为了方便,还可以将接受的答案翻译成模板函数:

    #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>)>);
    

    【讨论】:

    • 这当然不能避免评估双方,但如果主要问题是它们的类型不同,它可能非常方便。
    • 是的,我同意。 constexpr_if 的参数必须格式正确,才能与公认的答案相反。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-04-28
    • 2010-12-12
    • 2021-01-03
    • 2012-08-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多