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) 
Community
  • 1
  • 1
jww
  • 97,681
  • 90
  • 411
  • 885
  • 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`. – Sam Varshavchik Nov 12 '16 at 15:40
  • You are assigning to a `const char*` so your function needs to return a `const char*`. – Galik Nov 12 '16 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; }`? – jww Nov 12 '16 at 15:48

3 Answers3

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.

Community
  • 1
  • 1
krzaq
  • 16,240
  • 4
  • 46
  • 61
  • 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 Nov 12 '16 at 15:55
  • 1
    @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 *`). – vsoftco Nov 12 '16 at 16:00
  • 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 Nov 12 '16 at 16:49
  • @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. – krzaq Nov 12 '16 at 16:51
  • @jww in the end, you probably want `static constexpr const char* Name()` – krzaq Nov 12 '16 at 16:54
  • 1
    @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. – ildjarn Nov 13 '16 at 14:51
  • @ildjarn - Yeah, Clang seems to need them. Clang lags GCC and ICC in a set of benchmark suites by 2 to 3 seconds. Believe it or not, the warning above appeared after adding `constexpr`. Prior to it there was no warning. – jww Nov 13 '16 at 15:00
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.

Rakete1111
  • 47,013
  • 16
  • 123
  • 162
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() ...