I have a handy class in C++ that can be initialized either with an double or with a char*. Everything works as expected except one case when the argument is zero.
struct Var {
Var (double src) {}
Var (char *src) {}
};
int main(int argc, char* argv[]) {
Var A = 123;
Var B = "abc";
Var C = 0; <- Error: conversion from 'int' to 'Var' is ambiguous
}
Previously I used int instead of double, and it was fine for some reason.
How can this be fixed?
PS: I know I can use (double)0 or just 0.0, but is there a way to allow just 0 to be accepted as double?
You could eliminate the ambiguity by adding a third constructor which takes an int and delegates to the double version (requires C++11):
struct Var {
Var (double src) {}
Var (const char *src) {}
// ^^^^^ should be const
Var (int src) : Var {static_cast<double>(src)} {}
};
However, this could be solved easier by changing your char* constructor to take a std::string instead and using direct initialization.
In C++, the literal 0 has type int. You cannot change anything about it being an integer, without adding at least a decimal point. (no actual need to write digits after the point though)
To answer you implicit question regarding why the call was unambiguous with 123 but ambiguous with 0:
When you initialized A, it was fine because there is no implicit conversion from an int to a pointer (char*, in this case).
When you initialized C using an int the second time, you used 0, the literal 0 can be implicitly converted to a pointer (some legacy behaviour, before C++11 offered nullptr literal).
So, in a nutshell:
int * a = 0; // legal;
int * b = 2; // ILLEGAL;
When initializing C one conversion is needed to give the argument to any of the two available constructors (and both conversions have the same ranking), the call is ambiguous.
To make it unambiguous, you should either cast your int literal to a double ((double)0), use a double literal (0.), or add a constructor taking an int.
With this last solution, the constructor taking an int is an exact match, so it is a better match that another overload requiring a conversion (which is why it would be selected).
Edit. Please note that TartanLlama answer should be accepted here as it is a best C++ practice: the constructor taking an C-style string should take a std::string instead. Since there is an implicit conversion from C-style string to std::string, the call will work with little change:
Var B("abc");
But you will isolate your class interface from pointers details.
Related
The following code compiles under Clang/GCC under the C++17 standard, but does not compile under MSVC with -std:C++17 /Zc:ternary.
struct CStringPtr
{
const char *m_pString = nullptr;
CStringPtr() = default;
CStringPtr( const char *pString ) : m_pString( pString ) { }
operator const char *() const { return m_pString; }
};
int main( int argc, char ** )
{
bool b = !!argc;
const char *X = b ? CStringPtr( "inside" ) : "naked";
const char *Y = b ? "naked" : CStringPtr( "inside" );
CStringPtr Z = b ? CStringPtr( "inside" ) : "naked";
CStringPtr W = b ? "naked" : CStringPtr( "inside" );
// Silence unused-variable warnings.
return X && Y && Z && W;
}
Link to godbolt's compiler explorer with all three: https://godbolt.org/z/6d5Mrjnd7
MSVC emits an error for each of those four lines:
<source>(19): error C2445: result type of conditional expression is ambiguous: types 'const char [6]' and 'CStringPtr' can be converted to multiple common types
<source>(19): note: could be 'const char *'
<source>(19): note: or 'CStringPtr'
Whereas Clang/GCC each call the CStringPtr constructor for the naked string for all four of those cases.
In the MSVC /Zc:ternary documentation they claim that the flag enables standards-compliant resolution of the ternary operator, which implies that either there's a bug in MSVC's implementation or Clang/GCC are not standards-compliant here.
The one other note here is that the MSVC docs mention an exception in this exact case with respect to the const char * type being used:
An important exception to this common pattern is when the type of the operands is one of the null-terminated string types, such as const char*, const char16_t*, and so on. You can also reproduce the effect with array types and the pointer types they decay to. The behavior when the actual second or third operand to ?: is a string literal of corresponding type depends on the language standard used. C++17 has changed semantics for this case from C++14.
So, is MSVC non-compliant under C++17 rules? Or are Clang/GCC?
This is Core issue 1805, which changed ?: to decay arrays and functions to pointers before testing whether the other operand can be converted to it. The example in that issue is basically the one in your question:
struct S {
S(const char *s);
operator const char *();
};
S s;
const char *f(bool b) {
return b ? s : ""; // #1
}
One might expect that the expression in #1 would be ambiguous, since
S can be converted both to and from const char*. However, the
target type for the conversion of s is const char[1], not const char*, so that conversion fails and the result of the
conditional-expression has type S.
It appears that neither GCC nor Clang implements that issue resolution, so they are still testing whether CStringPtr can be converted to an array.
If you manually decay the string literals with a unary +, everyone rejects the example as ambiguous.
I was trying to understand the usage of explicit keyword in c++ and looked at this question on SO
What does the explicit keyword mean in C++?
However, examples listed there (actually both top two answers) are not very clear regarding the usage. For example,
// classes example
#include <iostream>
using namespace std;
class String {
public:
explicit String(int n); // allocate n bytes to the String object
String(const char *p); // initializes object with char *p
};
String::String(int n)
{
cout<<"Entered int section";
}
String::String(const char *p)
{
cout<<"Entered char section";
}
int main () {
String mystring('x');
return 0;
}
Now I have declared String constructor as explicit, but lets say if I dont list it as explicit, if I call the constructor as,
String mystring('x');
OR
String mystring = 'x';
In both cases, I will enter the int section. Until and unless I specify the type of value, it defaults to int. Even if i get more specific with arguments, like declare one as int, other as double, and not use explicit with constructor name and call it this way
String mystring(2.5);
Or this way
String mystring = 2.5;
It will always default to the constructor with double as argument. So, I am having hard time understanding the real usage of explicit. Can you provide me an example where not using explicit will be a real failure?
explicit is intended to prevent implicit conversions. Anytime you use something like String(foo);, that's an explicit conversion, so using explicit won't change whether it succeeds or fails.
Therefore, let's look at a scenario that does involve implicit conversion. Let's start with your String class:
class String {
public:
explicit String(int n); // allocate n bytes to the String object
String(const char *p); // initializes object with char *p
};
Then let's define a function that receives a parameter of type String (could also be String const &, but String will do for the moment):
int f(String);
Your constructors allow implicit conversion from char const *, but only explicit conversion from int. This means if I call:
f("this is a string");
...the compiler will generate the code to construct a String object from the string literal, and then call f with that String object.
If, however, you attempt to call:
f(2);
It will fail, because the String constructor that takes an int parameter has been marked explicit. That means if I want to convert an int to a String, I have to do it explicitly:
f(String(2));
If the String(char const *); constructor were also marked explicit, then you wouldn't be able to call f("this is a string") either--you'd have to use f(String("this is a string"));
Note, however, that explicit only controls implicit conversion from some type foo to the type you defined. It has no effect on implicit conversion from some other type to the type your explicit constructor takes. So, your explicit constructor that takes type int will still take a floating point parameter:
f(String(1.2))
...because that involves an implicit conversion from double to int followed by an explicit conversion from int to String. If you want to prohibit a conversion from double to String, you'd do it by (for example) providing an overloaded constructor that takes a double, but then throws:
String(double) { throw("Conversion from double not allowed"); }
Now the implicit conversion from double to int won't happen--the double will be passed directly to your ctor without conversion.
As to what using explicit accomplishes: the primary point of using explicit is to prevent code from compiling that would otherwise compile. When combined with overloading, implicit conversions can lead to some rather strange selections at times.
It's easier to demonstrate a problem with conversion operators rather than constructors (because you can do it with only one class). For example, let's consider a tiny string class fairly similar to a lot that were written before people understood as much about how problematic implicit conversions can be:
class Foo {
std::string data;
public:
Foo(char const *s) : data(s) { }
Foo operator+(Foo const &other) { return (data + other.data).c_str(); }
operator char const *() { return data.c_str(); }
};
(I've cheated by using std::string to store the data, but the same would be true if I did like they did and stored a char *, and used new to allocate the memory).
Now, this makes things like this work fine:
Foo a("a");
Foo b("b");
std::cout << a + b;
...and, (of course) the result is that it prints out ab. But what happens if the user makes a minor mistake and types - where they intended to type +?
That's where things get ugly--the code still compiles and "works" (for some definition of the word), but prints out nonsense. In a quick test on my machine, I got -24, but don't count on duplicating that particular result.
The problem here stems from allowing implicit conversion from String to char *. When we try to subtract the two String objects, the compiler tries to figure out what we meant. Since it can't subtract them directly, it looks at whether it can convert them to some type that supports subtraction--and sure enough, char const * supports subtraction, so it converts both our String objects to char const *, then subtracts the two pointers.
If we mark that conversion as explicit instead:
explicit operator char const *() { return data.c_str(); }
...the code that tries to subtract two String objects simply won't compile.
The same basic idea can/does apply to explicit constructors, but the code to demonstrate it gets longer because we generally need at least a couple of different classes involved.
I'm reading C++ Primer and in section 6.2 it says:
"Parameter initialization works the same way as variable
initialization."
Yet when I do:
void foo(char* args[]) {return;}
int main() {
char* args[]={"asd","dsa"}; // ok.
foo({"asd","dsa"}); // error.
}
Why is that?
As #T.C. pointed out in the comments, the args in the function argument is converted to a char** because functions can't take arrays as an argument. Since you can't do
char **asd={"asd","dsa"};
the code is illegal. My confusion came from the fact that
char* args[]={"asd","dsa"};
char **asd=args;
is legal.
It is generally possible to take advantage of the new initialization syntax and semantics to use anonymous arrays as arguments, but you will have to jump through a few hoops. For example
typedef const char *CC2[2];
void foo(const CC2 &a) {}
int main() {
foo({ "asd", "dsa" });
}
However, in your case this technique will not help because you are requesting an array-to-pointer conversion on a temporary array. This is illegal in C++.
typedef int A[2];
const A &r = A{ 1, 2 }; // reference binding is OK
int *p = A{ 1, 2 }; // ERROR: taking address is not OK
So, if you really want to do something like this, you can do the following
template <size_t N> void foo(const char *const (&args)[N]) {}
int main() {
foo({ "asd", "dsa" });
}
but that is not exactly what you had in mind originally.
"Parameter initialization works the same way as variable initialization."
This is true as far as calling constructors, I think they mean. However, initializers are special, and different from ordinary value expressions you can assign to a variable or pass to a function call.
C/C++ doesn't have a way to write a literal anonymous array. You can only do it as an initializer as you declare a variable.
Why is that?
First of all in both cases, you need char const* because you are working with string literals.
Secondly, {...} could work if the parameter type was an array, but char*[] is adjusted to char** (due to decayment) which cannot be initialised with a braced-init-list.
Alternatively use std::string and std::vector, as you are already supposed to do:
void foo(std::vector<std::string> args) {return;}
and:
foo({"asd","dsa"});
will work just fine.
I want to create a move constructor that takes string literal, and then move that c string to a member pointer.
The best solution I could write is giving a warning:
deprecated conversion from string constant to 'char*' [-Wwrite-strings]
CTextBlock cctb("move(H)");
^
the code:
#include <iostream>
using namespace std;
class CTextBlock
{
public:
CTextBlock(char* &&text)//move constructor
{
pText = text;
}
private:
char *pText;
};
int main()
{
CTextBlock cctb("move(H)"); //WARNING
return 0;
}
First off, the type of string literals is char const[N] (for a suitable constant N). This array can be assigned to a char const* in which case it will decay into a pointer to the first element. It cannot be converted to a char*. Prior to C++11 the conversion to char* was allowed to deal with existing code which wasn't const-correct (e.g., because it started as C code before C got const). This conversion was removed for C++11.
Question is what you actually try to achieve, though: string literals are immutable and persist for the entire life-time of the program. You can just keep as many pointers to them as you want and there is no point in moving pointers as these are entirely cheap to copy.
In your question you indicate that you want to create a move constructor but move constructors take an rvalue reference of the class they are for, e.g., this would be a move constructor for you class:
CTextBlock::CTextBlock(CTextBlock&& other)
: pText(other.pText) {
other.pText = 0;
}
(your class doesn't show any ownership semantics for the pointer pText in which case move construction doesn't really make much sense; the above code assumes that there is some ownership semantics and that a null pointer indicates that the object doesn't own anything).
Just because an argument is constrained to be an rvalue reference doesn't mean that function is a move constructor. All it implies is that the argument is an rvalue an it can reasonably be assume that it's current representation doesn't need to be retained. The string literal appears to be an rvalue because the the string literal is converted into a [temporary] pointer to the start of the array.
A constructor that gets reasonably close to allowing only literals can be realized as a template:
#include <cstddef>
#include <assert>
struct X
{
char const * str;
std::size_t len;
template <std::size_t N>
X(char const (&a)[N]) : str(a), len(N - 1)
{
assert(a[len] == '\0'); // true for string literals
}
};
It's not fool-proof, though, since it will also bind to named character arrays, and the length computation is dubious if your string also contains null values. But if you're trying to avoid accidental use of dynamic values (e.g. in an algorithm that builds strings from expressions and literal strings), this is fairly useful.
A string literal is a const char *. This is true whether or not it's used as an lvalue or an rvalue.
If you review your code again, you are, therefore, attempting to store a const char * into a char *, and that's where your compiler diagnostic is coming from. Your move constructor is taking an rvalue reference to a char *, and not a const char *. Change it to a const char *, and change the pText class member to a const char *, and what you're trying to do should work.
I'm not going to say I fully understand the idea of const correctness but let's at least say that I understand it. So, when I encountered this, I was/am stumped. Can someone please explain this to me. Consider the following code:
#include <iostream>
struct Test {
int someInt;
void * pSomething;
};
void TestFunc(Test * const pStruct) {
pStruct->someInt = 44;
pStruct->pSomething = "This is a test string"; // compiler error here
}
int main() {
Test t;
TestFunc(&t);
return 0;
}
At the point I've annotated with a comment I get this error from gcc (4.5.3 for cygwin):
foo.cpp:10:24: error: invalid conversion from `const void*' to `void*'
It's apparently something to do with the fact that the struct contains a void* because some experimentation revealed that changing the struct to:
struct Test {
int someInt;
char * pSomething;
};
produces a warning as opposed to an error. Also, leaving the structure unchanged but modifying this line to include the following cast compiles the code without warning:
pStruct->pSomething = (void*)"This is a test string"; // no error now
What I don't understand, given my understanding of const correctness, is why is the compiler emitting this error, "invalid conversion from ‘const void*’ to ‘void*’" at all? Since the const modifier on the function definition makes it so that the pointer is constant but what it points to is not, why should this be a problem? I'm assuming that there is some sort of implicit cast happening, as originally written, since the string literal would be something like const char * that must be converted to void *. That notwithstanding, the question remains since what pStruct points to is not constant.
For reference, I read up on const correctness here and here.
The error you are observing has absolutely nothing to do with const qualifier used in function declaration, or with any const qualifiers you explicitly used in your code.
The problem is the same as in the following minimal example
void *p = "Hello";
which suffers from the same error.
In C++ language the type of string literal is const char [N]. By the rules of const correctness it is convertible to const void *, but it is not convertible to void *. That's all.
A more formally correct error message would be "Cannot convert from const char [22] to void *", or "Cannot convert from const char * to void *", but apparently the inner workings of the compiler perform the conversion to const void * first (under the hood) and then stumble on conversion to void *, which is why the error message is worded that way.
Note that const correctness rules of C++ language used to include an exception that allowed one to implicitly convert string literals to char * pointers. This is why
char *p = "Hello";
compiles with a mere warning, even though it violates the rules of const correctness just like the previous example. That exception applied only to conversions to char * type, not to void * type, which is why the previous example produces an error. This special conversion has been deprecated in C++03 and removed from the language in C++11. This is why the compiler issues a warning about it. (If you switch your compiler to C++11 mode it will become an error.)
First of all, your test class and function just make things unnecessarily complex. In particular, the error has nothing to do with the const in Test * const pStruct, because this only means that pStruct must not be made to point to anything else. After all, in your own words:
the const modifier on the function definition makes it so that the
pointer is constant but what it points to is not
Here is a simplified piece of code to reproduce the problem:
int main() {
void *ptr = "This is a test string"; // error
}
As for your question,
What I don't understand, given my understanding of const correctness,
is why is the compiler emitting this error, "invalid conversion from
‘const void*’ to ‘void*’" at all? Since the const modifier on the
function definition makes it so that the pointer is constant but what
it points to is not, why should this be a problem?
Because a string literal is a char const[], "decaying" to a char const *, and the conversion to a non-constant pointer would lose the const qualifier.
This does not work for the same reason the following won't:
int main() {
int const *i; // what's pointed to by i shall never be modified
void *ptr = i; // ptr shall modify what's pointed to by i? error!
}
Or more precisely,
int main() {
int const i[22] = {}; // i shall never be modified
void *ptr = i; // ptr shall modify i? error!
}
If this was not an error, then you could use ptr to implicitly bypass the const qualifier of i. C++ simply does not allow this.
Finally, let's look at this piece of code:
pStruct->pSomething = (void*)"This is a test string"; // no error now
Again, this can be simplified and reproduced with int rather than char so as not to obfuscate the real issue:
int main() {
int const i[22] = {}; // i shall never be modified
void *ptr = (void*)i; // ptr shall modify i? ok, I'll just shut up
}
You should not use C-style casts in C++. Use one of static_cast, reinterpret_cast, dynamic_cast and const_cast to make it clear which kind of conversion should be enforced.
In this case, you'd have seen the trouble it takes to "shut up" the compiler:
int main() {
int const i[22] = {};
void *ptr = const_cast<void*>(reinterpret_cast<void const *>(i));
}
And, of course, even though this may compile without a warning, the behaviour of the program is undefined because you must not use const_cast to cast away the constness of an object originally initialised as constant.
Edit: I forgot about the whole char * C compatibility business. But this is covered in the other answers already and, to my best understanding, does not render anything in my answer incorrect.
First of all, using C style casts will break const correctness. That is the only reason that your cast "works". So don't do it. Use reinterpret_cast, which (should, I didn't test it) get you a similar error to the one you are seeing.
So "This is a test string" is a const char*. If you reference it as a void*, something could modify the contents of void* later. If you make it so you can mess with the contents of that, you're no longer const correct.
And you COULD if there was no error, shown below.
int main() {
Test t;
TestFunc(&t);
reinterpret_cast<char*>(t.pSomething)[0]='?';
return 0;
}
"blah" is an array of 5 char const. In C++11 it converts implicitly to char const*. In C++03 and earlier the literal also converted implicitly to char*, for C compatibility, but that conversion was deprecated, and it was removed in C++11.
Setting
void* p = "blah"; // !Fails.
you get the conversion sequence char const[5] → char const* → void*, where the last one is invalid and yields an error.
Setting
char* p = "blah"; // Compiles with C++03 and earlier.
with C++11 you get the same conversion as for void*, and an error, but with C++03 and earlier, since the source is a literal string you get char const[5] → char*.