【问题标题】:How can a variable be both constexpr and not constexpr?变量如何既是 constexpr 又不是 constexpr?
【发布时间】:2016-09-27 00:36:48
【问题描述】:

我创建了一个constexpr 字符串类型,我称之为StaticString。我从this 网站得到这个想法。

编译器在将变量视为 constexpr 在一行上,然后在下一行不是 constexpr 时遇到了一些奇怪的问题。

代码如下:

constexpr StaticString hello = "hello";
constexpr StaticString hello2 = hello + " ";
constexpr StaticString world = "world";
constexpr StaticString both = hello + " world";
constexpr StaticString both2 = hello2 + world;
//This works fine (world is constexpr?)

//constexpr StaticString both3 = "hello " + world; 
//ERROR: "world" is not constexpr

int main(void)
{
    static_assert(hello[4] == 'o' ,"ERROR");
    static_assert(hello == "hello", "ERROR");
    static_assert(both2 == "hello world", "ERROR");
}

这里是StaticString的定义:

class StaticString{
        const char* const str;
        const size_t len;
        const StaticString* head;
    public:
        template<size_t N>
        constexpr StaticString(const char(&aStr)[N]) 
            : str(aStr), len(N-1), head(nullptr)  //Chop off the null terminating char
        {
            static_assert(N>=1,"String cannot have a negative length");
        }
        template<size_t N>
        constexpr StaticString(const char(&aStr)[N] ,const StaticString* ss) : head(ss), str(aStr),len(N-1) { }
        constexpr StaticString(const char* const aStr ,const size_t len,const StaticString* ss = nullptr) 
            : str(aStr), len(len), head(ss)
        {
        }
        constexpr char GetFromHead(size_t index) const{
            return index < head->GetSize() ? (*head)[index] : str[index - head->GetSize()];
        }
        constexpr char operator[](size_t index) const{
            return head ? GetFromHead(index) : str[index];
        }
        constexpr size_t GetSize() const{
            return head ? len + head->GetSize() : len;
        }
        constexpr bool Equals(const char* const other,size_t len,size_t index = 0) const{

            return (other[0] == (*this)[index]) ? (len > 1 ? Equals(&other[1],len-1,index+1) : true) : false;
        }
        template<size_t N>
        constexpr bool operator==(const char(&other)[N]) const{
            return Equals(other,N-1);
        }
        template<size_t N>
        constexpr StaticString operator+(const char(&other)[N]) const{
            return StaticString(other,this);
        }
        constexpr StaticString operator+(StaticString other) const{
            return StaticString(other.str,other.len,this);
        }

};
template<size_t N>
constexpr StaticString operator+(const char(&str)[N],const StaticString& other){
    return StaticString(str) + other;
}

所以我的问题是:为什么world 在一行上被视为constexpr 而不是下一行?

注意: 这是我得到的错误:

'StaticString{((const char*)"world"), 5ull, ((const prototypeInd::util::StaticString*)(&<anonymous>))}' is not a constant expression

我也在使用gcc

【问题讨论】:

  • 另外,hello2 + world + world 也会失败。
  • 您使用的是什么编译器? VS2015 不会在both3 的声明/定义上产生错误,但是当它在static_assert() 中使用时确实会使编译器崩溃...(C1001:编译器中发生了内部错误。)所以我猜这真的没什么帮助。
  • 你解决了吗?用您所做的更正更新问题怎么样?

标签: c++ constexpr compile-time-constant


【解决方案1】:

讨论

您的 world 变量是 constexpr 但表达式中的 operator+

constexpr StaticString both3 = "hello " + world;

虽然标记为constexpr 不是。因为在它的返回语句中:

return StaticString(str) + other;

由于创建了临时StaticString(str) 指针,指向非静态存储持续时间的临时对象也正在创建中。这归因于这样一个事实,即在您的 StaticString 对象中,您正在存储非静态存储持续时间临时对象的地址,并且常量表达式中不允许使用此类指针。

理由

根据标准§5.20/p5 常量表达式[expr.const]Emphasis Mine):

常量表达式要么是左值核心常量表达式 其值指的是一个实体,该实体是一个允许的结果 常量表达式(定义如下),或纯右值核心常量 其值满足以下约束的表达式:

(5.1) — 如果值是类类型的对象,则每个非静态数据 引用类型的成员指的是允许的实体 常量表达式的结果,

(5.2) — 如果值是指针类型,它包含一个地址 具有静态存储持续时间的对象,地址超过此类末尾 一个对象(5.7),一个函数的地址,或者一个空指针值, 和

(5.3) — 如果值是类或数组类型的对象,每个 子对象满足这些值的约束。

一个实体是一个常量表达式的允许结果,如果它是一个 具有不是临时的静态存储持续时间的对象 对象或者是一个临时对象,其值满足上述条件 约束,或者它是一个函数。

.

【讨论】:

  • @Cornstalks,因为左边不是StaticString
  • 另外,“other + str”与“str + other”不同。
  • @zneak 评论是正确的 Corntalks 指的是我的第一个错误答案:(
  • 不幸的是,zneak 说我无法使用您的解决方案,因为 other + stringstring + other 不同,这是创建字符串“hello world”和“worldhello”之间的区别
  • 我想你想用std::array-like 系列替换一个静态字符串,其中连接构建一个更大的包含数据。然后指向非静态存储的指针问题就消失了。字符串实际上也是在编译时连接的。不幸的是,缺少operator"" 的字符串的char... 重载意味着我们不能轻易地将字符串纯粹存储在类型中。