8

I have made a constexpr string type, which I call StaticString. I got this idea from this website.

I am having some weird issues with the compiler treating a variable as a constexpr on one line, and then not a constexpr on the next line.

Here is the code:

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");
}

And here is the definition of 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;
}

So my question is this: why does world get treated as a constexpr on one line but not the next?

NOTE: This is the error I get:

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

Also I am using gcc

SCFrench
  • 8,244
  • 2
  • 31
  • 61
DarthRubik
  • 3,927
  • 1
  • 18
  • 54
  • Additionally, `hello2 + world + world` will also fail. – Cornstalks May 28 '16 at 22:47
  • What compiler(s) are you using? VS2015 doesn't generate an error on the declaration/definition of `both3`, but it does crash the compiler when it's used in a `static_assert()`... (C1001: An internal error has occurred in the compiler.) So I guess that's really not much help. – Michael Burr May 28 '16 at 22:53
  • Did you solve it? What about updating the question with the corrections you made? – zertyz Dec 14 '18 at 00:45

1 Answers1

7

Discussion

Your world variable is constexpr but operator+ in expression:

constexpr StaticString both3 = "hello " + world;

although marked as constexpr is not. Because in its return statement:

return StaticString(str) + other;

due to the creation of the temporary StaticString(str) pointers to non-static-storage duration temporaries are also being created. This is attributed to the fact that in your StaticString objects you're storing addresses of non-static-storage duration temporaries and these kind of pointers are not allowed in constant expressions.

Justification

According to the standard §5.20/p5 Constant expressions [expr.const] (Emphasis Mine):

A constant expression is either a glvalue core constant expression whose value refers to an entity that is a permitted result of a constant expression (as defined below), or a prvalue core constant expression whose value satisfies the following constraints:

(5.1) — if the value is an object of class type, each non-static data member of reference type refers to an entity that is a permitted result of a constant expression,

(5.2) — if the value is of pointer type, it contains the address of an object with static storage duration, the address past the end of such an object (5.7), the address of a function, or a null pointer value, and

(5.3) — if the value is an object of class or array type, each subobject satisfies these constraints for the value.

An entity is a permitted result of a constant expression if it is an object with static storage duration that is either not a temporary object or is a temporary object whose value satisfies the above constraints, or it is a function.

.

101010
  • 41,839
  • 11
  • 94
  • 168
  • @Cornstalks, because the left-hand side is not a `StaticString`. – zneak May 28 '16 at 22:57
  • Also, "other + str" is not the same as "str + other". – zneak May 28 '16 at 22:58
  • @zneak that comment is correct Corntalks was referring to my first mistaken answer :( – 101010 May 28 '16 at 22:59
  • As zneak said unfortunately I cannot use your solution because `other + string` is different than `string + other` this is the difference between creating the strings "hello world" and " worldhello" – DarthRubik May 28 '16 at 23:30
  • @DarthRubik hmm... you're right, I'll try to give another solution. – 101010 May 28 '16 at 23:41
  • 2
    I think you want to replace the one staticstring with a `std::array`-like family, where concatination builds a larger one *containing the data*. Then the pointers-to-non-static-storage problem evaporates. Strings are actually concatinated at compile time as well. Unfortunetally the lack of the `char...` overload of `operator""` for strings means we cannot easily store the string purely in the type. – Yakk - Adam Nevraumont May 29 '16 at 00:34
  • @Yakk The only problem is you cannot do this scenario at compile time with constexpr (I think?) – DarthRubik May 30 '16 at 14:54
  • @DarthRubik I see no reason not? You would use factory functions that take a `char const(&)[N]` and thus know the type to be produced from incoming type information. `operator+( static_string, static_string )` returns `static_string`. So the types are easy. Data shouldn't be any harder? So you'd have `constexpr auto hello = StaticString("hello");`. Sadly user defined literals `"hello"_static` won't work as it lacks the right `operator""` overload. – Yakk - Adam Nevraumont May 30 '16 at 14:57
  • @Yakk I will look into into....if I succeed I will let you know...thanks for the idea – DarthRubik May 30 '16 at 15:14