【问题标题】:Why are 'constexpr' parameters not allowed?为什么不允许使用“constexpr”参数?
【发布时间】:2015-02-19 22:04:15
【问题描述】:

拥有 'constexpr' 参数以区分编译器已知值并能够在编译时检测错误会很有用。例子:

int do_something(constexpr int x)
{
  static_assert(x > 0, "x must be > 0");
  return x + 5;
}

int do_something(int x)
{
  if(x > 0) { cout << "x must be > 0" << endl; exit(-1); }
  return x + 5;
}

int var;

do_something(9); //instance 'do_something(constexpr int x)' and check arg validity at compile-time

do_something(0); //produces compiler-error

do_something(var); //instance 'do_something(int x)'

目前这是一个无效的代码。有人可以解释一下为什么这不能实现吗?

编辑:

使用模板的用户应该确保文字总是作为模板参数而不是作为非常不舒服的函数参数传递:

template<int x>
int do_something()
{
  static_assert(x > 0, "x must be > 0");
  return x + 5;
}

int do_something(int x)
{
  if(x > 0) { cout << "x must be > 0" << endl; exit(-1); }
  return x + 5;
}

int var;

do_something(9); //instance 'do_something(int x)' and doesn't checks validity at compile-time

do_something(0); //same as above, if check was performed - compiler error should occur

do_something<9>(); //instance template 'do_something<int>()'

do_something<0>(); //produces compiler error

do_something(var); //instance 'do_something(int x)'

【问题讨论】:

  • 你不能用模板做到这一点吗?更具体地说是使用非类型模板参数?
  • 那么我的函数的用户应该提供不同的语法来调用它,具体取决于参数是否在编译时已知。
  • 如果传入 constexpr 值,assert() 可能会被优化。
  • gcc 4.8.3 (cygwin x64) with -std=c++11 无法识别 constexpr 参数的使用。那是标准吗?我对 C++ 14 没有经验,但 constexpr-ness 是否可能是函数的一个属性,它可能会或可能不会在编译时计算,具体取决于参数的(隐式)constexpr-ness?
  • constexpr 不是类型限定符。你想要它吗?我们已经有了constvolatile,它们一起提供了 4 种组合。我们真的需要更多吗?

标签: c++ language-lawyer c++14 compile-time


【解决方案1】:

如果我理解您正在尝试正确执行的操作,则您请求的功能已经可用。这不是最优雅的,但我认为这已经足够了。

您希望在编译时和运行时使用相同的语法调用函数,并尽可能在编译时对其进行评估,否则应在运行时进行评估。无论何时调用,您都需要对函数进行评估。

我相信这会如你所愿:

constexpr int do_something(int x)
{
    if(x <= 0)
    {
        std::cout << "x must be > 0" << std::endl; exit(-1);
    }
    return x + 5;
}

constexpr int compiletime_good = do_something(5);
constexpr int compiletime_bad = do_something(0);    // Fails at compile-time

int runtime_good = do_something(5);
int runtime_bad = do_something(0);    // Fails at runtime

constexpr int val_good = 5;
constexpr int val_bad = 0;

do_something(val_good);
do_something(val_bad);    // Fails at run-time

int valrun_good = 5;
int valrun_bad = 0;

do_something(valrun_good);
do_something(valrun_bad);    // Fails at run-time

这里的技巧是在编译时以不需要 static_assert 的方式失败,并且在运行时也会失败。

【讨论】:

  • 这将要求在需要 'constexpr' 的地方调用 'do_something' 的每个实例,这并不比使用模板更好。
  • @FISOCPP 够公平的。我猜我们没有明确的方法以您所要求的方式包含 constexpr 参数的原因是没有人费心编写并推动提案。我希望看到这种情况发生,但听起来工作量很大。
  • 当然应该使用现有的工具来报告断言失败,而不是在整个代码库中散布std::exit() 调用。否则这是非常好的。
  • 改进它的唯一方法是,如果参数是编译时常量,即使不是在constexpr 上下文中运行,也可能会导致编译器警告。也许整数除以零? 1 / (x &gt; 0); 当然,编译器是否警告 UB 是非常特定于编译器的。
  • @BenVoigt 谢谢,我只是使用 OP 的代码,所以我使用了他们的运行时失败代码。
【解决方案2】:

虽然这在理论上听起来很棒,但在现实世界中并没有那么有用。函数的大多数参数都不是编译时常量,而且许多约束在编译时也不完全清楚。

指定和实现这样的重载将是大量的工作,而且不会被大量使用。当您实际上具有编译时界限和参数时,通常可以在编译时评估整个函数,这意味着不需要重载。

【讨论】:

  • 这并没有具体回答为什么,这是问题所要问的。
  • @cybermonkey 我显然不同意。我试图通过描述为什么它没有足够用来证明努力的合理性来解释为什么该功能不在标准中。与此问题的其他答案不同,后者没有以任何方式、形状或形式回答原因,而只是试图描述一种解决方法(即放置实际代码,只是展示我在第二段中写的内容)。跨度>
猜你喜欢
  • 2017-12-29
  • 2017-05-27
  • 1970-01-01
  • 2023-03-27
  • 2018-04-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多