constexpr function returning string literal - c++

A function returning a copy of an integer literal
int number()
{ return 1; }
can be easily converted to a plain compile-time expression using the keyword constexpr.
constexpr int number()
{ return 1; }
However, I'm getting confused when it comes to string literals. The usual method is returning a pointer to const char that points to the string literal,
const char* hello()
{ return "hello world"; }
but I think that merely changing "const" to constexpr is not what I want (as a bonus, it also produces the compiler warning deprecated conversion from string constant to 'char*' using gcc 4.7.1)
constexpr char* hello()
{ return "hello world"; }
Is there a way to implement hello() in such a way that the call is substituted with a constant expression in the example below?
int main()
{
std::cout << hello() << "\n";
return 0;
}

const and constexpr are not interchangeable, in your case you do not want to drop the const but you want to add constexpr like so:
constexpr const char* hello()
{
return "hello world";
}
The warning you receive when you drop const, is because a string literal is an array of n const char and so a pointer to a string literal should be a *const char ** but in C a string literal is an array of char even though it is undefined behavior to attempt to modify them it was kept around for backwards compatibility but is depreciated so it should be avoided.

Force constexpr evaluation:
constexpr const char * hi = hello();
std::cout << hi << std::endl;

Related

Imcplicit conversion from string literal to unsigned int (bool) [duplicate]

I am trying to write a C++ class that has some overloaded methods:
class Output
{
public:
static void Print(bool value)
{
std::cout << value ? "True" : "False";
}
static void Print(std::string value)
{
std::cout << value;
}
};
Now lets say I call the method as follows:
Output::Print("Hello World");
this is the result
True
So, why, when I have defined that the method can accept boolean and string, does it use the boolean overload when I pass in a non-boolean value?
EDIT: I come from a C#/Java environment, so quite new to C++!
"Hello World" is a string literal of type "array of 12 const char" which can be converted to a "pointer to const char" which can in turn be converted to a bool. That's precisely what is happening. The compiler prefers this to using std::string's conversion constructor.
A conversion sequence involving a conversion constructor is known as a user-defined conversion sequence. The conversion from "Hello World" to a bool is a standard conversion sequence. The standard states that a standard conversion sequence is always better than a user-defined conversion sequence (§13.3.3.2/2):
a standard conversion sequence (13.3.3.1.1) is a better conversion sequence than a user-defined conversion sequence or an ellipsis conversion sequence
This "better conversion sequence" analysis is done for each argument of each viable function (and you only have one argument) and the better function is chosen by overload resolution.
If you want to make sure the std::string version is called, you need to give it an std::string:
Output::Print(std::string("Hello World"));
Not sure why nobody posted this, but you can add another overload that converts from const char* to std::string for you. This saves the caller from having to worry about this.
class Output
{
public:
static void Print(bool value)
{
std::cout << value ? "True" : "False";
}
static void Print(std::string value)
{
std::cout << value;
}
// Just add the override that cast to std::string
static void Print(const char* value)
{
Output::Print(std::string(value));
}
};
FWIW, it can be addressed this way (if templates can be used), if you don't want to add overloads for const char*.
#include <iostream>
#include <string>
#include <type_traits>
template <typename Bool,
typename T = std::enable_if_t<std::is_same<Bool, bool>{}>>
void foo(Bool)
{
std::cerr << "bool\n";
}
void foo(const std::string&)
{
std::cerr << "string\n";
}
int main()
{
foo("bar");
foo(false);
}
Since C++14 we have the operator""s from the std::string_literals namespace, which can be used to tell the compiler to bind to the string (or string_view in C++17) overload:
using namespace std::string_literals;
Output::Print("Hello World"s);
Prints: Hello World

Function overloading for const char*, const char(&)[N] and std::string

What I want to achieve is to have overloads of a function that work for string literals and std::string, but produce a compile time error for const char* parameters. The following code does almost what I want:
#include <iostream>
#include <string>
void foo(const char *& str) = delete;
void foo(const std::string& str) {
std::cout << "In overload for const std::string& : " << str << std::endl;
}
template<size_t N>
void foo(const char (& str)[N]) {
std::cout << "In overload for array with " << N << " elements : " << str << std::endl;
}
int main() {
const char* ptr = "ptr to const";
const char* const c_ptr = "const ptr to const";
const char arr[] = "const array";
std::string cppStr = "cpp string";
foo("String literal");
//foo(ptr); //<- compile time error
foo(c_ptr); //<- this should produce an error
foo(arr); //<- this ideally should also produce an error
foo(cppStr);
}
I'm not happy, that it compiles for the char array variable, but I think there is no way around it if I want to accept string literals (if there is, please tell me)
What I would like to avoid however, is that the std::string overload accepts const char * const variables. Unfortunately, I can't just declare a deleted overload that takes a const char * const& parameter, because that would also match the string literal.
Any idea, how I can make foo(c_ptr) produce a compile-time error without affecting the other overloads?
This code does what is needed (except the array - literals are arrays, so you can't separate them)
#include <cstddef>
#include <string>
template <class T>
void foo(const T* const & str) = delete;
void foo(const std::string& str);
template<std::size_t N>
void foo(const char (& str)[N]);
int main() {
const char* ptr = "ptr to const";
const char* const c_ptr = "const ptr to const";
const char arr[] = "const array";
std::string cppStr = "cpp string";
foo("String literal");
//foo(ptr); //<- compile time error
// foo(c_ptr); //<- this should produce an error
foo(arr); //<- this ideally should also produce an error
foo(cppStr);
}
In order for your deleted function to not be a better match than the template function, so that string literals still work, the deleted function needs to also be a template. This seems to satisfy your requirements (though the array is still allowed):
template <typename T>
typename std::enable_if<std::is_same<std::decay_t<T>, const char*>::value>::type
foo(T&& str) = delete;
Demo.
In modern versions of the language you may create some custom type and user-defined literal that will create it, so that it will be possible to pass "this"_SOMEWORDS, but not just c string literal, chat pointer or char array.
It doesn't exactly satisfy your requirements to pass string literal but I think it's good enough, especially because it forbids also arrays

Storing std::to_string(x).c_str() in member variable produces garbage

I have this very simple C++ program:
using namespace std;
class TheClass
{
private:
const char *_numberString;
public:
TheClass(int number)
{
_numberString = to_string(number).c_str();
}
operator const char *()
{
return _numberString;
}
};
int main(int argc, const char * argv[])
{
TheClass instance = 123;
cout << (const char *)instance << endl;
return 0;
}
When I run it in Xcode, it logs \367\277_\377. If I change it to this however:
using namespace std;
class TheClass
{
public: // Change 1/2
const char *_numberString;
public:
TheClass(int number)
{
_numberString = to_string(number).c_str();
}
operator const char *()
{
return _numberString;
}
};
int main(int argc, const char * argv[])
{
TheClass instance = 123;
instance._numberString = to_string(123).c_str(); // Change 2/2
cout << (const char *)instance << endl;
return 0;
}
it logs 123 like it should. I can't see what I'm doing wrong. Even if I change 123 to another number the exact same thing is logged.
At this point
_numberString = to_string(number).c_str();
you are storing a pointer to the interned data of a temporary std::string value, that is invalidated after that line of code.
Accessing _numberString effectively calls undefined behavior.
As mentioned in comments, there's no point to keep the _numberString1 member as const char*. Use a std::string member instead:
class TheClass {
private:
std::string numberString_;
public:
TheClass(int number) : numberString_(to_string(number)) {
}
operator const std::string& () {
return numberString_;
}
};
1) You shouldn't use prefixed _ for class member names, that's reserved for compiler and standard implementation intrinsics. If you dislike patterns like m_ or other prefix conventions (like me), just use a postfix _ as shown in my sample.
The return value of c_str is only valid for as long as the string is in scope (and unaltered). Your anonymous temporary goes out of scope at the end of the statement.
Consider having a std::string as a member variable rather than a pointer type, or store the numeric value itself.
c_str() returns a pointer to the buffer of the std::string instance it's call on. The object returned by std::to_string() is a temporary and is destroyed at the end of the constructor body. That leaves _numberString pointing to an object that has since been destroyed.
The second piece of code doesn't have to work. You have the same problem as in the first one. The fact that it works is an effect of undefined behavior.

String literal vs const char* function overload

I have a function that I want to work for const char*'s but it only works for string literals because they are given a special rule to be allowed to initialize arrays. The second overload, foo(const char*) will be preferred for both string literals and const char*s, but my template overload will not work for const char*s.
// Errors for const char*.
template<typename T, size_t n>
void foo(const T (&s)[n])
{
}
// Selected both times if both overloads are present.
void foo(const char*)
{
}
int main()
{
foo("hello");
const char* s = "hello";
foo(s); // Error if foo(const char*) is absent.
}
Is there a way to allow const char*s to initialize arrays?
I've tried this:
template<typename T, size_t n>
void _foo(const T (&s)[n])
{
std::cout << __PRETTY_FUNCTION__;
}
#define _expand(s) #s
#define expand(s) _expand(s)
void foo(const char* s)
{
_foo(expand(s));
}
I think that
const char* s = "hello";
is a pointer to the string literal somewhere in read only memory, and compiler cannot deduce the array size, so it chooses second overload.
You can use
const char s[] = "hello";
No, you can't initialize an array with a pointer. The string literal syntax is a special shorthand for a const char s[] and your working code roughly equivalent to
static const char s[]{'h','e','l','l','o','\0'};
foo(s);
So, you can pass arrays to your template function, including string literals, but you cannot pass pointers, including pointers to string literals.
On the other hand, arrays can decay to pointers, which is why both arrays and pointers can be passed to your second overload.

String literal matches bool overload instead of std::string

I am trying to write a C++ class that has some overloaded methods:
class Output
{
public:
static void Print(bool value)
{
std::cout << value ? "True" : "False";
}
static void Print(std::string value)
{
std::cout << value;
}
};
Now lets say I call the method as follows:
Output::Print("Hello World");
this is the result
True
So, why, when I have defined that the method can accept boolean and string, does it use the boolean overload when I pass in a non-boolean value?
EDIT: I come from a C#/Java environment, so quite new to C++!
"Hello World" is a string literal of type "array of 12 const char" which can be converted to a "pointer to const char" which can in turn be converted to a bool. That's precisely what is happening. The compiler prefers this to using std::string's conversion constructor.
A conversion sequence involving a conversion constructor is known as a user-defined conversion sequence. The conversion from "Hello World" to a bool is a standard conversion sequence. The standard states that a standard conversion sequence is always better than a user-defined conversion sequence (§13.3.3.2/2):
a standard conversion sequence (13.3.3.1.1) is a better conversion sequence than a user-defined conversion sequence or an ellipsis conversion sequence
This "better conversion sequence" analysis is done for each argument of each viable function (and you only have one argument) and the better function is chosen by overload resolution.
If you want to make sure the std::string version is called, you need to give it an std::string:
Output::Print(std::string("Hello World"));
Not sure why nobody posted this, but you can add another overload that converts from const char* to std::string for you. This saves the caller from having to worry about this.
class Output
{
public:
static void Print(bool value)
{
std::cout << value ? "True" : "False";
}
static void Print(std::string value)
{
std::cout << value;
}
// Just add the override that cast to std::string
static void Print(const char* value)
{
Output::Print(std::string(value));
}
};
FWIW, it can be addressed this way (if templates can be used), if you don't want to add overloads for const char*.
#include <iostream>
#include <string>
#include <type_traits>
template <typename Bool,
typename T = std::enable_if_t<std::is_same<Bool, bool>{}>>
void foo(Bool)
{
std::cerr << "bool\n";
}
void foo(const std::string&)
{
std::cerr << "string\n";
}
int main()
{
foo("bar");
foo(false);
}
Since C++14 we have the operator""s from the std::string_literals namespace, which can be used to tell the compiler to bind to the string (or string_view in C++17) overload:
using namespace std::string_literals;
Output::Print("Hello World"s);
Prints: Hello World