【问题标题】:Why is this not a constant expression?为什么这不是一个常量表达式?
【发布时间】:2014-08-26 04:07:29
【问题描述】:

在这个简单的示例中,即使test1 成功,test2 也无法编译,我不明白为什么会这样。如果arr[i] 适用于标记为constexpr 的函数的返回值,那么为什么不能将其用作非类型模板参数?

template<char c>
struct t
{ 
    static const char value = c;
};

template <unsigned N>
constexpr char test1(const char (&arr)[N], unsigned i)
{
    return arr[i];
}

template <unsigned N>
constexpr char test2(const char (&arr)[N], unsigned i)
{
    return t<arr[i]>::value;
}

int main()
{
   char a = test1("Test", 0); //Compiles OK
   char b = test2("Test", 0); //error: non-type template argument 
                              //is not a constant expression
}

编辑:这没什么区别:

template<char c>
struct t
{ 
    static const char value = c;
};

template <unsigned N>
constexpr char test1(const char (&arr)[N])
{
    return arr[0];
}

template <unsigned N>
constexpr char test2(const char (&arr)[N])
{
    return t<arr[0]>::value;
}

int main()
{
   char a = test1("Test"); //Compiles OK
   char b = test2("Test"); //error: non-type template argument 
                           //is not a constant expression
}

【问题讨论】:

    标签: c++ templates c++11 constexpr c++14


    【解决方案1】:

    简答:C++11/14 中没有constexpr 函数参数。

    更长的答案:在test1() 中,如果i 不是编译时常量,则该函数在运行时仍然可用。但在test2()中,编译器无法知道i是否为编译时常量,但函数编译需要它。

    例如test1 的以下代码将编译

    int i = 0;    
    char a = test1("Test", i); // OK, runtime invocation of test1()
    
    constexpr int i = 0;
    constexpr char a = test1("Test", i); // also OK, compile time invocation of test1()
    

    让我们简单地将您的test2() 发送给

    constexpr char test3(unsigned i)
    {
        return t<i>::value;
    }
    

    这不会为test3(0) 编译,因为在test3() 内部,不能证明i 是一个无条件 编译时表达式。您需要constexpr 函数参数才能表达这一点。

    引用标准

    5.19 常量表达式 [expr.const]

    2 条件表达式 e 是核心常量表达式,除非 e 的评估,遵循抽象机 (1.9) 的规则, 将评估以下表达式之一:

    — 一个 id 表达式,它引用一个变量或数据成员 引用类型,除非该引用具有前面的初始化和 要么
    — 用常量表达式初始化或

    ——它是一个对象的非静态数据成员,其生命周期从 e 的求值开始;

    本节有以下代码示例对应您的问题:

    constexpr int f1(int k) {
        constexpr int x = k; // error: x is not initialized by a
                             // constant expression because lifetime of k
                             // began outside the initializer of x
        return x;
    }
    

    因为上面例子中的x不是一个常量表达式,这意味着你不能在f1里面使用x或者k来实例化模板。

    【讨论】:

    • 事实并非如此。如果您从 test2 中删除 unsigned i 并改为索引为 arr[0] 您仍然会遇到相同的编译错误。
    • 例如,如果您在 test2 中尝试 t&lt;test1(arr,i)&gt;::value,请查看编译器的内容。显然 test1 在这种情况下不是 constexpr,因为arri 在一般情况下是运行时参数。该函数必须编译它们是否是文字。
    • “constexpr 函数参数”是否可以添加到语言中?
    • 除非有人写了提案,否则不会。我认为它已在 std-proposals 新闻组中提出。但是constexpr lambdas 例如是我的愿望清单上更高的东西。
    【解决方案2】:

    对于constexpr 在这里所做的事情存在误解。它表明函数必须在编译时对合适的参数进行评估,但它确实删除了在一般情况下仍要编译的要求。

    我们来看第一个版本:

    template <unsigned N>
    constexpr char test1(const char (&arr)[N], unsigned i) {
        return arr[i];
    }
    

    现在,这显然是一个编译时评估:

    enum { CompileTimeConstant = test1("Test", 0) };
    

    您的示例可能是,但这是一个优化器/QoI 问题:

    char MayBeCompileTimeConstant = test1("Test", 0);
    

    这个例子显然不是,但仍然需要可评估

    char arr[10];
    int i;
    std::cin >> i;
    std::cin >> arr;
    char b = test1(arr, i);
    std::cout << "'" << arr << "'[" << i << "] = " << b << '\n';
    

    由于test2 不可能在最后一种情况下编译,所以它根本无法编译。 (请注意,我并不是说代码是)。

    【讨论】:

      【解决方案3】:

      这里的问题是调用arr[i] 会调用下标运算符operator[]此运算符不返回常量表达式。

      其实不是constexpr的问题,是模板参数推导的问题。非类型模板参数必须是常量表达式,而下标运算符的返回参数不是。

      因此,编译器正确地抱怨arr[i] 不是一个常量表达式。

      【讨论】:

        【解决方案4】:

        因为arr[i] 不是编译时常量表达式。在运行时可能会有所不同。

        【讨论】:

        • 事实并非如此。如果您从 test2 中删除 unsigned i 并改为索引为 arr[0] 您仍然会得到相同的编译错误。
        • @Chris_F arr[i](甚至arr[0])不是编译时常量,即使它是const char[]
        • @Chris_F error: an array reference cannot appear in a constant-expression
        【解决方案5】:

        您可以使用动态数组大小来修复它。您可以为此使用矢量类,因为在矢量类中您可以动态调整列表的大小。或者您可以使用 malloc ,但是当向量类站在那里时,这会浪费时间。都是我说的

        【讨论】:

        • 您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center
        猜你喜欢
        • 2019-11-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-08-27
        • 2022-10-08
        • 2011-11-15
        相关资源
        最近更新 更多