Implicit Conversion from Char** to Const Char**

Implicit conversion from char** to const char**

Such a conversion would allow you to put a const char* into your array of char*, which would be unsafe. In print you could do:

thing[0] = "abc";

Now argv[0] would point to a string literal that cannot be modified, while main expects it to be non-const (char*). So for type safety this conversion is not allowed.

Why C doesn't allow implicit conversion from char ** to const char *const * (and C++ does)?

C and C++ are different in this respect. I don't have an answer to why C++ is more generous, other than that the C++ behaviour seems to me to be correct.

C simply doesn't allow indirect const conversion. That is a conservative, easy-to-implement restriction, with the unfortunate consequence that you cannot provide char*[] to a function expecting char const* const*. The restriction is in §6.3.2.3, paragraph 2, and it is simply not recursive:

For any qualifier q, a pointer to a non-q-qualified type may be converted to a pointer to the q-qualified version of the type; the values stored in the original and converted pointers shall compare equal.

C++ allows conversions according to a somewhat complex formulation in §4.4 [conv.qual], paragraph 3. It is permitted to convert

T cvn Pn-1cvn-1 … P1cv1 P0cv0

T cv'n Pn-1cv'n-1 … P1cv'1 P0cv'0

(where T is a type; P1…Pn are pointer/array type constructors, and each cv0…cvn is some possibly empty subset of const and volatile)

provided that:

  1. For every k > 0, cvk is a subset of cv'k (so you can't remove a const or a volatile), and

  2. If cvk and cv'k differ for some k > 0, all the following cv'i>k include const.

In the actual standard, that expression is reversed; I put it in the order of declaration, whereas in the standard it is in order of application of the pointer/array constructors. I didn't change the direction of the numbering, though, which is why they are numbered right to left. I also left out some details -- for example, it's not strictly necessary for the two Ts to be identical -- but I think it gives an idea of the intention.

The explanation for the first restriction is reasonably obvious. The second restriction prevents the problem described in the C FAQ, where a const pointer might be stored into a non-const pointer object, and then subsequently used to mutate the const object it points to.

The bottom line is that in C++, your prototype const char *const * param will work with arguments of type char**, const char**, or even char*const*, but in C only the last one will work without warning, and it is the least useful. The only workaround I know of (other than switching to C++) is to ignore the warning.

For what it's worth, there is a note in the Rationale section of the Posix specification of the exec* interfaces about the problem this causes for these prototypes, and the workaround selected by Posix, which is to use char*[] as the prototype and textually note that these are constant: (emphasis added)

The statement about argv[] and envp[] being constants is included to make explicit to future writers of language bindings that these objects are completely constant. Due to a limitation of the ISO C standard, it is not possible to state that idea in standard C. Specifying two levels of const-qualification for the argv[] and envp[] parameters for the exec functions may seem to be the natural choice, given that these functions do not modify either the array of pointers or the characters to which the function points, but this would disallow existing correct code. Instead, only the array of pointers is noted as constant.

There's a useful compatibility chart following that paragraph, which I didn't quote because of the formatting limitations of this site.

Why const char* implicitly converted to bool rather than std::string?

Because the implicit conversion from const char* to bool is qualified as standard conversion, while const char* to std::string is user-defined conversion. The former has higher ranking and wins in overload resolution.

A standard conversion sequence is always better than a user-defined conversion sequence or an ellipsis conversion sequence.

BTW: mystruct obj(c); performs direct initialization, explicit converting constructors including mystruct::mystruct(bool) are considered too. As the result, c is converted to bool then passed to mystruct::mystruct(bool) as argument to construct obj.

Direct-initialization is more permissive than copy-initialization: copy-initialization only considers non-explicit constructors and non-explicit user-defined conversion functions, while direct-initialization considers all constructors and all user-defined conversion functions.

About explicit specifier,

  1. Specifies that a constructor or conversion function (since C++11) or deduction guide (since C++17) is explicit, that is, it cannot be used for implicit conversions and copy-initialization.

Explain: Converting 'char **' to 'const char **', Conversion loses qualifiers

Yes, you cannot implicitly convert from a T ** to a const T **, because the compiler can no longer guarantee that the const-ness won't be violated.

Consider the following code (borrowed from the C FAQ question on exactly this topic: Why can't I pass a char ** to a function which expects a const char **?):

const char c = 'x';
char *p1;
const char **p2 = &p1; // 3
*p2 = &c;
*p1 = 'X'; // 5

If the compiler allowed line 3, then line 5 would end up writing to a const object.

Why is it not possible to implicitly convert char** to const char **

because if it was allowed then you could inadvertently change something that was declared const.

here's a concrete abuse example, if the rules allowed this:

char const* s = "a literal, very const";

bar(const char** pp )
{
*pp = s;
}

foo(char** arr)
{
bar(arr);
char* unconsted_s = *arr;
unconsted_s[0] = 'X';
}

This is also a FAQ. It’s often a good idea to check the FAQ (or just google) before asking.

Why doesn't std::string provide implicit conversion to char*?

From the C++ Programming Language 20.3.7 (emphasis mine):

Conversion to a C-style string could have been provided by an operator const char*() rather than c_str(). This would have provided the convenience of an implicit conversion at the cost of surprises in cases in which such a conversion was unexpected.

Conversion from `const char[]` to non-scalar type requested

The problem is that two class-type conversions are required:

  • const char[6] to std::string
  • std::string to field<std::string>.

There is a rule that implicit conversion can have at most one class-type conversion (the official term is "user-defined" conversion although this includes class types that are part of the standard library).

To fix it you can either use your suggested fix; or manually specify one of the conversions, e.g:

auto strB = field<std::string>("Hello");
field<std::string> strC = std::string("Hello");
field<std::string> strD = "Hello"s;

or you could add a constructor for const char[].

SFINAE version of char array constructor (there will be better ways in C++20 I'm sure):

template<size_t N, typename = std::enable_if_t<std::is_same_v<T, std::string>>>
field(char const (&t)[N])
: t_(std::make_unique<T>(t)) {}

Implicit conversion from char to int for constructors in C++

But C++ standard allows only 1 implicit conversion.

Thats not correct.

From cppreference:

Implicit conversion sequence consists of the following, in this order:

  1. zero or one standard conversion sequence;
  2. zero or one user-defined conversion;
  3. zero or one standard conversion sequence.

From the language point of view, const char[N] -> std::string (or const char* to std::string) is a user-defined conversion. Hence, the commented out lines are errors. On the other hand,

A obj_5 = ch;               // How is this working?

is fine, because there is only a single user-defined conversion involved.

Invalid conversion from ‘const char*’ to ‘unsigned char*’

In C++ string literals have types of constant character arrays. For example string literal "123" has type const char[4].

In expressions with rare exceptions arrays are converted to pointers to their first elements.

So in this declaration

unsigned char* t="123";

the initializer has type const char *. There is no implicit conversion from const char * to unsigned char *

You could write

const unsigned char* t = reinterpret_cast<const unsigned char *>( "123" );

Warning about using explicit keyword in a conversion operator

I can think of one unintentional implicit conversion here. If you were to accidentally write

Date date;
// ...
if (date) { /* ... */ }

the compiler could first convert date to const char*, and then const char* to bool, and this would be a difficult-to-debug situation. The explicit operator protects against this situation.

There's a better way to define how your class should be printed to std::cout:

class Date {
// ...
std::string toString() const {
// ...
return formattedDate.str();
}
}

std::ostream& operator<<(std::ostream& out, const Date& date) {
return out << date.toString();
}

If you must use a conversion operator, why not make it explicit and convert where needed? It would be easier to see from the code exactly what is happening.

std::cout << static_cast<const char*>(date);


Related Topics



Leave a reply



Submit