为什么这不是一个常数表达式

Why is this not a constant expression?

本文关键字:一个 常数 表达式 这不是 为什么      更新时间:2023-10-16

在这个简单的示例中,即使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++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中实例化xk的模板。

对于constexpr在这里的作用存在误解。它表明函数必须在编译时对合适的参数可求值,但是并没有消除在一般情况下仍然需要编译的要求。

让我们看第一个版本:

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

现在,这显然是一个编译时的求值:

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

你的例子可能是,但这是一个优化器/qi问题:

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不可能在最后一种情况下编译,它根本不能编译。(请注意,我不是建议代码)。

这里的问题是调用arr[i]会调用下标操作符operator[]

该操作符不返回常量表达式。

这实际上不是constexpr的问题,是模板参数演绎的问题。非类型模板实参必须是下标操作符的返回实参不是的常量表达式。

因此,编译器有理由抱怨arr[i]不是一个常量表达式。

因为arr[i]不是编译时常量表达式。

你可以用动态数组大小来修复它。你可以使用vector类,因为在vector类中你可以动态地调整列表的大小。或者你可以使用malloc,但是当向量类还在那里的时候,这样做会浪费时间。