为什么这不是一个恒定的表达?

时间:2014-07-04 21:04:21

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

在这个简单的例子中,即使test2成功,test1也无法编译,我不明白为什么会这样。如果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
}

4 个答案:

答案 0 :(得分:22)

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

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

E.g。 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不是常量表达式,因此意味着您无法在x内实例化kf1的模板。

答案 1 :(得分:7)

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无法为最后一种情况编译,因此根本无法编译。 (请注意,我并不是说代码)。

答案 2 :(得分:2)

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

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

因此,编译器理所当然地抱怨arr[i]不是常量表达式。

答案 3 :(得分:1)

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