0

I'm trying to understand what I am seeing while testing a change. The platform is openSUSE 42 with GCC 4.8, but it could affect others. The test code and error follows.

$ cat test.cxx 
#include <string>

#if (__cplusplus >= 201103L)
#  define STATIC_CONSTEXPR static constexpr
#  define CONSTEXPR constexpr
#else
#  define STATIC_CONSTEXPR static const
#  define CONSTEXPR
#endif

struct Name
{
  STATIC_CONSTEXPR char* GetName() {return "XXX";}
};

int main(int argc, char* arv[])
{
  const char* name = Name::GetName();
  return 0;
}

And:

$ g++ -O3 -std=c++11 test.cxx -o test.exe
test.cxx: In static member function ‘static constexpr char* Name::GetName()’:
test.cxx:13:44: warning: deprecated conversion from string constant to ‘char*’ [-Wwrite-strings]
   STATIC_CONSTEXPR char* GetName() {return "XXX";}
                                            ^

Adding the following does not clear it:

struct Name
{
  STATIC_CONSTEXPR char* const GetName() {return "XXX";}
};

And adding the following does clear it:

struct Name
{
  STATIC_CONSTEXPR const char* GetName() {return "XXX";}
};

According to Does static constexpr variable make sense?:

Every variable declared constexpr is implicitly const but const and static are almost orthogonal (except for the interaction with static const integers.)

I thought I mostly understood constexpr but I'm obviously missing something (again). As I understand it, the C++ committee believes a value like "XXX" in the reproducer can somehow change after the file is saved even though its impossible under the laws of the physical universe as we currently understand them. However, to combat the problem, they gave us constexpr. An alternate explanation is here, but I have to admit I don't see the finer details that makes the difference.

Why am I seeing the warning, and why do I effectively need static constepr const to squash it?


$ uname -a
Linux opensuse-42 4.1.27-27-default #1 SMP PREEMPT Fri Jul 15 12:46:41 UTC 2016 (84ae57e) x86_64 x86_64 x86_64 GNU/Linux
opensuse-42:~$ gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib64/gcc/x86_64-suse-linux/4.8/lto-wrapper
Target: x86_64-suse-linux
Configured with: ../configure --prefix=/usr ... --host=x86_64-suse-linux
Thread model: posix
gcc version 4.8.5 (SUSE Linux) 
3
  • 4
    No, you don't understand the problem. " a value like "XXX" in the reproducer can somehow change after the file is saved " is a meaningless statement, that doesn't have anything to do with it. A literal string constant, "XXX" is a const char *, and your failing code attempts to return it as a char *. A pointer to a constant object cannot be converted to a pointer to a mutable object, implicitly. This has nothing to do with constexpr. Commented Nov 12, 2016 at 15:40
  • You are assigning to a const char* so your function needs to return a const char*. Commented Nov 12, 2016 at 15:43
  • So I am clear... The value returned from the static function marked as a constexpr is not implicitly const. Is that correct? That begs the question, should the function body instead be ... Name() { STATIC_CONSTEXPR char val[] = "XXX"; return val; }? Commented Nov 12, 2016 at 15:48

3 Answers 3

8

constexpr on a function declaration means that the function is constexpr, not its return type.

So your problem really boils down to the following syntax being wrong:

char * foo = "bar";

Since C++11 this conversion from char const[] to char* is flat out illegal, and has been deprecated since C++98.

Making the pointer const, as opposed to pointer to const is explored more in-depth here.

So, to return a string literal you need to use char const*. static and constexpr are not related to this problem at all.

Sign up to request clarification or add additional context in comments.

7 Comments

Please forgive my ignorance... What is an example of a variable or a function return value marked as contexpr but allowed to be non-const? I.e., the caller can modify it. It makes no sense to me; they seem to be orthogonal.
@jww You can use a constexpr function in non-constant contexts too. That's why it was probably decided that constexpr functions should not return const by default. And even if they would have returned const, in your case the pointer itself would've been const (like char * const), not the data it points to (char const *).
Thanks @krzaq. So does C++11 want static constexpr char* constexpr Name()? The outer one says, the function is not going to change after the file is saved; while the inner one says the data in the array is not going to change after the file is saved?
@jww No, no. A function returning a string literal should return char const* (or const char*, it's the same thing). You can also decorate the function with static and constexpr keywords, changing its semantics, but this is orthogonal to the return type, which is forced.
@jww : You seem really hung up on constexpr; in fact, your problem has absolutely nothing to do with constexpr at all. Set aside the constexpr distraction and focus on the real issue: string literals are made up of constant characters.
|
3

A string literator "..." is of type const char[], i.e. a array to a const characters. It can decay to const char* if needed.

You're getting the warning because you are using a deprecated conversion from const char* to char*/char* const. It doesn't matter that the function is constexpr, it only applies to the function itself, not the return value. It doesn't make sense to specify constexpr on the return value, because it is either already constexpr because of the function, or not.

So, your code is equivalent of writing

char* return_value = "XXX";
char* const return_value = "XXX";

Because none of those pointers point to a const char, you'll get a warning.

Comments

2

constexpr has nothing to do with it.

char *foo() { return "ABC"; }

will give the same warning. "ABC" is a string literal, and is of type const char[4] (array of four constant char). Like any other array, it will readily decay into a pointer of type char const * (pointer to constant char).

For string literals (only), there is also a deprecated conversion to char * (pointer to non-const char) - that is what your function is using, and that is what GCC doesn't like. The fix is either:

char const *foo() ...

or (equivalently)

const char *foo() ...

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.