Overload char pointers and string literals - c++

I'm trying to create a class that will accept (and can disambiguate) string literals from char pointers. When searching this problem the solution provided was to create a 'holder' class that only accepts char pointers:
template< typename char_t >
class String
{
struct holder_t
{
const char_t* s;
holder_t( const char_t* s ) : s(s) {}
holder_t( char_t* s ) : s(s) {}
};
public:
// Construct from string literal
template< size_t S >
String( const char_t (&str)[S] )
{
std::cout << "String Literal: " << str;
}
// Construct from char pointer
String( holder_t h )
{
std::cout << "Char Pointer: " << h.s;
}
};
The big idea is that you should never have to explicitly create instances of this class, it should happen by implicit conversion:
void StringFoo( String<char> s )
{
}
However this results in compile errors for char pointers, why isn't the implicit conversion working?
int main()
{
// Works!
StringFoo( "literal" );
// error C2664: 'StringFoo' : cannot convert parameter 1 from 'const char *' to 'String<char_t>'
// with [ char_t=char ]
// No constructor could take the source type, or constructor overload resolution was ambiguous
StringFoo( (const char*)"const char ptr" );
// Works!
StringFoo( String<char>((const char*)"const char ptr") );
// error C2664: 'StringFoo' : cannot convert parameter 1 from 'char *' to 'String<char_t>'
// with [ char_t=char ]
// No constructor could take the source type, or constructor overload resolution was ambiguous
StringFoo( (char*)"char ptr" );
// Works!
StringFoo( String<char>((char*)"const char ptr") );
return 0;
}
This is just a simple pointless example; in my real code I created a class that will hold the hash of the string.
The hash should be generated at compile time if a string literal is passed and calculated at runtime if a char pointer is passed.
However this should be completely transparent to the user who just passes strings around, hashing is done automagically.

The reason the code doesn't work with pointer is that it requires two versions (char const* to holder and holder to String. However, implicit conversions will do at most one conversion.
For the purpose you are describing I'd think you can reasonably just distinguish between char const(&)[N] and char const*: since you always want to compute a hash anyway and just make it is a constexpr where possible, making the version taking char const(&)[N] a reference should do the trick.

To get from a string literal to a String requires two user-defined conversions - one to holder_t and another to String. But overload resolution only allows you to have one such conversion per argument.

Related

Error with setter method: argument of type "const char *" is incompatible with parameter of type "char *"

I was trying to make a setter for name in some class, but I'm having a problem with this.
void Cats::setName(char* s) {
if (this->name != NULL) {
delete[] name;
name = new char[strlen(s + 1)];
strcpy_s(s, strlen(name) + 1, name);
}
}
That's the setter, and I can't write any name for any Cat of mine.
Cats::Cats() {
setName("NoName"); <----- problem here and in other constructors also.
setWeight(0.6);
setAge(0);
}
argument of type "const char *" is incompatible with parameter of type "char *"
How can I make this happen? What am I missing? An explanation would be great.
"NoName" is a const char*. The const means that no one can modify the characters in it. Your function takes a char*, which is a mutable character array. You can't pass constant data to a function whose signature requires mutable data. If you really don't intend to modify the array, then take a const char*
void Cats::setName(const char* s)
or, even better, as Bathesheba pointed out, use std::string, which will manage the memory for you.
void Cats::setName(std::string s)
Using new and delete directly, in modern C++, is generally a code smell and should be avoided. We have std::string for strings, std::array and std::vector for arrays, and std::unique_ptr and std::shared_ptr for owned heap memory. Let your types speak for themselves, and the compiler will take care of memory management for you.
For starters this statement in the setter
name = new char[strlen(s + 1)];
is incorrect. It should look like
name = new char[strlen(s ) + 1];
Nevertheless the function should be defined at least the following way
void Cats::setName( const char *s ) {
if (this->name != nullptr) delete[] name;
size_t n = strlen( s ) + 1;
name = new char[n];
strcpy_s(s, n, name);
}
You are calling the function passing to it a string literal
Cats::Cats() {
setName("NoName"); <----- problem here and in other constructors also.
setWeight(0.6);
setAge(0);
}
In C++ opposite to C string literals have types of constant character arrays. Thus the parameter of the setter shall be declared like
const char *s
and the constructor should look at least like
Cats::Cats() : name( nullptr ) {
setName("NoName");
setWeight(0.6);
setAge(0);
}
provided that in the class definition the data member name is not initialized explicitly with nullptr.

error:no appropriate constructor found, even though i have made one,c++

class String
{
private:
static const int SZ = 80;
char str[SZ];
public:
String()
{
strcpy_s(str, " ");
}
String(char s[])
{
strcpy_s(str, s);
}
}
This is the constructor i have written, note the second one
and here I am using it:
String s1 = "yes";
String s2 = "no";
String s3;
This is the error message :
Severity Code Description Project File Line Suppression State Error
(active) E0415 no suitable constructor exists to convert from "const
char [4]" to "String" rishabh c++ projects F:\rishabh c++
projects\rishabh c++ projects\main.cpp 35
The compiler is telling you there is no implicit cast from your string literal to any of the available constructors. The reason behind this is the types that your class can be constructed with:
String(char s[]);
Note that string literals are constant, as per the C++ standard. That will not be implicitly converted to the non-constant type that your constructor requires. To do that would require const_cast, but please don't do that!!!
You would also get an error (or at least a warning) if you attempted explicit construction:
String s1("yes"); // error
The simple fix is the change the parameter to const char[] (or indeed const char* is more commonly used).
String(const char *s)
{
strcpy_s(str, s);
}
Whenever passing pointer or reference types as parameters, always ask yourself: "does my function need to modify the value of the parameter?" -- whenever the answer is "no" make it const.

Error: "2 overloads have similar conversions"

EDIT: As I was writing the question I noticed that the method std::string GetNodeValue(const std::string& nodePath, const char * defaultValue) wasn't const. As LogicStuff also mentioned in his comment, adding the const qualification resolved the ambiguity.
I know this question was already been asked and answered properly here and several other times. I understand the underlying problem but I can't quite figure out why it is happening in this particular case and it has awoken my curious self.
I have the following class:
class ConfigurationReader
{
public:
// ...
std::string GetNodeValue(const std::string& nodePath, const char * defaultValue)
{
const std::string temp(defaultValue);
return GetNodeValue(nodePath, temp);
}
template <typename T> T GetNodeValue(const std::string & nodePath, T defaultValue) const
{
boost::optional<T> nodeValue = configuration.getNodeValueNothrow<T>(nodePath);
if ( nodeValue )
{
return *nodeValue;
}
LogConfigurationProblemsCri(logger, "Node not found: " << nodePath << ", Default value: " << defaultValue);
return defaultValue;
}
// ...
};
The template method has also several specializations for the types int16_t, uint16_t, and so on up to uint64_t.
It works like a charm when used:
string someValue = configurationReaderPtr->GetNodeValue("some_noe", "");
uint32_t otherValue = configurationReaderPtr->GetNodeValue("other_node", 11000);
bool yetAnother = configurationReaderPtr->GetNodeValue("other_node", true);
Except in one case:
uint32_t otherValue = configurationReaderPtr->GetNodeValue("other_node", 0);
The error I keep getting is:
"2 overloads have similar conversions
could be 'std::string ConfigurationReader::GetNodeValue(const std::string &,const char *)' or 'uint32_t ConfigurationReader::GetNodeValue(const std::string &,uint32_t) const'"
I tried casting the "default" value: uint32_t(0), static_cast<uint32_t>(0), 0U without any luck.
I should point out that I already found a workaround:
uint32_t otherValue = 0;
otherValue = configurationReaderPtr->GetNodeValue("other_node", otherValue);
But this doesn't answer my curiosity. I am currently using Microsoft Visual Studio 2012 Express and boost 1.54 libraries.
Any thoughts?
This is, because 0 is the literal for an empty pointer (which in modern C++ is replaced by "nullptr").
So 0 can be either an int or an empty pointer, especially a char*
Edit to add some reference:
You can find this in the standard as
4.10 Pointer conversions
A null pointer constant is an integer literal (2.13.2) with value zero or a prvalue of type std::nullptr_t
(the last one is the reference to nullptr)
Both overloads are considered equally viable for
configurationReaderPtr->GetNodeValue("other_node", 0);
because:
requires an implicit conversion from 0, which is of type int to const char *.
requires an implicit conversion from ConfigurationReader* to ConfigurationReader const* (to call const-qualified member function)
After making both overloads (equally) const-qualified, the code compiles (the function template is preferred). The 1st overload also does not modify any members in the first place.
Live on Coliru
In your particular example, it seems prudent to change signature of string version to
std::string GetNodeValue(const std::string& nodePath, const std::string defaultValue)
This will remove any ambiguity.

why string has to be converted to c_str (c string) when passing to printf?

I am new to C++
and I know I shouldn't be using printf in c++ while I have cout but this was for experiment sake.
My Question here is Why we have to convert a string to c_str (c string) while passing to printf in c++ while it works fine without converting in cout.
Below is my code
#include<iostream>
#include<stdio.h>
using namespace std;
class A{
int i;
string str;
public:
A (int value, const string & s) : i(value), str(s){};// constructor
// setters
void setvalue(int value) {i = value;}
void setstr(const string & s) {str = s;}
//geters
int get_value() {return i;}
string get_str() {return str;}
const char *get_str_cstr() {return str.c_str();}// I didn't get why we have to declare constant
};
int main(){
// new code
A obj1 = {11, "Jill"};
cout<<"value is : "<<obj1.get_value()<<" string is "<<obj1.get_str()<<endl;
// Now we wil change the values in A
obj1.setvalue(2);
obj1.setstr("Jack");
cout<<"value after change is : "<<obj1.get_value()<<" string after change is "<<obj1.get_str()<<endl;
// now we will use printf where get_str dosen't not work
//Error: for below commented printf function
/*In function 'int main()':|
error: cannot pass objects of non-trivially-copyable type 'std::string {aka class std::basic_string<char>}' through '...'|
||=== Build finished: 1 errors, 0 warnings (0 minutes, 0 seconds) ===|
*/
//printf("Value is %d and String is %s",obj1.get_value(),obj1.get_str());
// hence we declare a new char * get_str_cstr to make it work in printf;
printf("Value is %d and String is %s",obj1.get_value(),obj1.get_str_cstr());
return 0;}
I have also provided the error in program comments.
Thank you!
printf comes from C library, which predates objects, templates, and function overloading. When you specify %s format, the function takes an address of a null-terminated character sequence, and prints it. printf has no idea where the string comes from. In fact, it has no idea of its parameter types, because it uses variable-length parameter list feature.
std::string is a C++ string. Calling c_str() on it produces a pointer to the beginning of a C string, which is suitable for passing to printf and other functions expecting a C string.
cout, on the other hand, has been built with classes and overloading in mind. There is a special overload for operator << for std::string, which lets cout and other output streams extract characters from a C++ string.
printf is originally from C, which does not have std::string, so the analogous argument type is const char* which is what you get when you call .c_str()
The reason std::cout works with std::string is because operator<< is defined for that class.
Because printf() has no idea what a std::string is. The protypes for printf() are
int printf( const char* format, ... );
int fprintf( std::FILE* stream, const char* format, ... );
int sprintf( char* buffer, const char* format, ... );
int snprintf( char* buffer, std::size_t buf_size, const char* format, ... );
The reason std::string works with cout is that std::string provieds operator << which works with cout
string is a class in c++ stl.
c_str() is a member function of class string .
The signature is:
const _CharT* c_str() const { return _M_start; }
now, coming to printf, its signature is:
int printf ( const char * format, ... );
now, as long as you give it an argument that meets const char * format, it accepts it.

How to initialize "unsigned char *" with default argument in C++?

I have a class with a method with the following signature:
void print(unsigned char *word);
I need to set "" as default value for word, how can I do that?
I tried the obvious void print(unsigned char *word=""); but I got the following error:
error: cannot initialize a parameter of type
'unsigned char *' with an lvalue of type 'const char [1]'
void print(unsigned char *word="");
Since I can't initialize word with a string literal who should I do it?
You say that this is a "prefix" argument to apply to the printing.
The answer is that you should make the argument const, stop doing whatever mutations you're doing to it inside the function, and then use "" as a default argument:
void print(const char* prefix = "")
try
unsigned char empty[] = { 0 };
void print(unsigned char* word = empty )
{
...
}
"" yields an array of const char, whereupon you want an array of or pointer to NON-const unsigned char, both the type and the cv-qualification don't fit.
Note also that in C++ char != signed char and char != unsigned char.
Possibly you mean void print(const char *word);, but probably you want just print(std::string const &) or print(std::string).
void print();
void print(unsigned char* prefix);
// in cpp file:
void print(){
unsigned char temp = 0;
print(&temp);
}
This provides two overloads, one with 0 and one eith one argument.
The zero argument one has some automatic storage memory it provides to the one argument one. Note that only the single byte under the pointer is valid to read/write: without a length, print has no way to know any different anyhow.
While there is no array, none is needed for a single element.