【问题标题】:Conditional constexpr on non-dependent condition非依赖条件上的条件 constexpr
【发布时间】:2021-05-29 02:59:52
【问题描述】:

假设我有一个配置函数要由库的用户定义,它可能是也可能不是constexpr

constexpr int iterations() { return 10; }
// Or maybe:
int iterations() { return std::atoi(std::getenv("ITERATIONS")); }

// I'm okay with requiring the constexpr version to be:
constexpr auto iterations() { return std::integral_constant<int, 10>{}; }

现在我有一个函数根据iterations() 的值具有不同的行为,但是如果iterations 是,这个函数应该是constexpr,因为我希望用户能够在constexpr 中使用它如果他们将库配置为允许:

/*constexpr*/ std::uint32_t algorithm(std::uint32_t x) {
    auto const iterations = ::iterations();

    // For illustration purposes
    for (int i = 0; i < iterations; ++i) {
        x *= x;
    }

    return x;
}

如果iterations()constexpr,我可以对algorithm 做些什么来创建函数constexpr?简而言之,我想要像constexpr(constexpr(::iterations())) 这样的东西。


请注意,“有条件的constexpr”通常依赖于模板参数,如Conditionally constexpr member function,在这种情况下可以使用constexpr关键字,但在这种情况下,我希望constexpr是有条件的不是模板参数的东西,而是一个静态已知的函数。将algorithm 标记为constexprcompilation error

error: call to non-'constexpr' function 'bool iterations()'

【问题讨论】:

标签: c++ constexpr c++20 c++-concepts


【解决方案1】:

您可以通过确保存在 一些 组模板参数和函数参数来使编译器诊断保持沉默,即使 ::iterations() 不是 一个常量表达式。例如:

template <bool True = true>
constexpr std::uint32_t algorithm(std::uint32_t x) {
    if constexpr (True) {
        auto const iterations = ::iterations();

        for (int i = 0; i < iterations; ++i) {
            x *= x;
        }

        return x;
    } else {
        return 0;
    }
}

algorithm&lt;false&gt;(meow) 是一个常量表达式,只要meow 是,所以编译器不能抱怨 (https://godbolt.org/z/GvE9ME)。

【讨论】:

  • 不过,这种做法是对规则的嘲弄,不是吗?有点像,不,你不能这样做 static_assert(false); 但是是的,你可以这样做 static_assert(always_false_v&lt;T&gt;);
  • @Barry 是的。如果有一种更明确的方法可以选择退出该语言为模板提供的一些“有用”检查,那就太好了,但如果没有这种方法,我们就有了棘手的解决方法。 template &lt;bool B&gt; struct S { template &lt;bool BB = B, class = enable_if_t&lt;BB&gt;&gt; // ... 是经典案例——我们在核心语言中使用 requires-clauses 修复了它——但肯定不是唯一的案例。
【解决方案2】:

从您的回答中获得灵感,您可以使用概念来强制 iteration() 返回一个整数常量。

重要的部分是记住algorithm()ha 是一个模板函数或概念(requires)不能工作,所以你可以强加一个未使用和默认的模板参数

//VVVVVVVVVVVVVVVVVVVVVVVVVV <-- add this to activate `requires`  
  template <typename = void>
  constexpr std::uint32_t algorithm(std::uint32_t x)
          requires is_integral_constant<decltype(::iterations())>
   { return ::algorithm_impl(x, ::iterations()); }

以下是您的简化示例

template <typename T>
constexpr bool is_integral_constant = false;

template <typename T, T value>
constexpr bool is_integral_constant<std::integral_constant<T, value>> = true;

constexpr std::uint32_t algorithm_impl(std::uint32_t x, int iterations) {
    for (int i = 0; i < iterations; ++i) {
        x *= x;
    }

    return x;
}

template <typename = void>
constexpr std::uint32_t algorithm(std::uint32_t x)
        requires is_integral_constant<decltype(::iterations())>
 { return ::algorithm_impl(x, ::iterations()); }

std::uint32_t algorithm(std::uint32_t x) {
    return ::algorithm_impl(x, ::iterations());
}

【讨论】:

  • 我相当肯定你可以在非模板上使用requires(你当然可以在模板类中使用非模板),并且 GCC 的抱怨只是他们的概念实现不完整.无论如何,这是解决编译器针对约束实例化algorithm 主体的问题的好方法。
  • @Justin - 根据this page,“类模板、函数模板和非模板函数(通常是类模板的成员)可能与一个约束相关联,它指定了对模板参数的要求” ,所以(如果我理解正确的话)“模板参数”是必需的。
  • @Justin - 超现实的一面是,如果不涉及模板参数,这个解决方案也可以工作。
【解决方案3】:

我可以要求 constexpr 版本为:...std::integral_constant&lt;int, 10&gt;

因为您可以要求函数的 constexpr 版本具有不同的返回类型,所以我们可以检测这种特殊类型并通过使用约束条件来设置 constexpr(C++20 的 requires) .请注意,我们必须将主体进一步包裹在 if constexpr 中,因为编译器仍然会检查函数的主体:

template <typename T>
constexpr bool is_integral_constant = false;

template <typename T, T value>
constexpr bool is_integral_constant<std::integral_constant<T, value>> = true;

constexpr std::uint32_t algorithm_impl(std::uint32_t x, int iterations) {
    for (int i = 0; i < iterations; ++i) {
        x *= x;
    }

    return x;
}

constexpr std::uint32_t algorithm(std::uint32_t x)
        requires is_integral_constant<decltype(::iterations())> {
    if constexpr (is_integral_constant<decltype(::iterations())>) {
        return ::algorithm_impl(x, ::iterations());
    } else {
        // Unreachable, but enough to convince the compiler that there is a
        // constexpr friendly path through the function
        return 0xDEADBEEF;
    }
}

std::uint32_t algorithm(std::uint32_t x) {
    return ::algorithm_impl(x, ::iterations());
}

Demo

【讨论】:

  • 问题:如果你强加requires is_integral_constant&lt;decltype(::iterations())&gt;,为什么你在体内检查if constexpr (is_integral_constant&lt;decltype(::iterations())&gt;)?从来都不是真的吗?
  • 我的意思是:简单的 constexpr std::uint32_t algorithm(std::uint32_t x) requires is_integral_constant&lt;decltype(::iterations())&gt; { return ::algorithm_impl(x, ::iterations()); } 有什么问题?
  • @max66 是的,它总是正确的,但是没有检查并且只有return :;algorithm_impl(x, ::iterations()),编译器会抱怨constexpr函数永远不会是constexpr(唯一的路径调用非constexpr @ 987654334@)
  • 这是我不明白的部分:“唯一的路径调用非constexpr ::iterations()”。鉴于 (require) decltype(::iterations()) 是一个整数常数,怎么可能是“非 constexpr”?
  • @max66 见godbolt.org/z/ooY8bW。我不知道标准中的什么使这种情况发生,但确实如此(它甚至可能是 clang 的概念实现中的一个错误)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-01-26
  • 2014-08-06
  • 2019-09-18
  • 2014-06-10
  • 2011-01-13
  • 1970-01-01
相关资源
最近更新 更多