14

I'm making a C++ library which relies heavily on RTTI (customizable bridge to another language) and is very confused about string literal type.

This is a simple test I made to show the problem:

std::cout << typeid(const char*).name() << std::endl; // PKc
std::cout << std::any("").type().name() << std::endl; // PKc
std::cout << typeid("").name() << std::endl;          // A1_c

For me it looks like the first two print the type for const char*, but the last one is an array.

Why do results for std::any("").type() and typeid("") differ? Is there a way to get the first behavior, i.e. make results for string literals consistent (I use type identification to call different type handlers)?

P.S.: tests are done using Clang version 8.0.0-3 (tags/RELEASE_800/final) on Ubuntu 19.04.

Boann
  • 48,794
  • 16
  • 117
  • 146
val - disappointed in SE
  • 1,475
  • 3
  • 16
  • 40
  • 10
    `""` isn't a `const char*`, so they don't have matching types. A `""` is a `const char[]`, an array of const char. – Eljay Jun 12 '19 at 14:17
  • 1
    @Eljay Question isn't whether they are or shouldn't be different types. That's a premise of the question. The question is why `std::any("").type()` is not array (A1_c). – eerorika Jun 12 '19 at 14:21
  • 4
    To add to @Eljay's comment: the 2nd line (`std::any`) is a pointer because the char array decays to a pointer when it's passed to the constructor. – Timo Jun 12 '19 at 14:21
  • 1
    @Timo it's passed by reference, so it doesn't decay when passed. – Aykhan Hagverdili Jun 12 '19 at 15:07

3 Answers3

17

As others have mentioned, the type of the string literal "" is const char[1], as explained by, e.g., What is the datatype of string literal in C++?.

The type stored in std::any("") is const char* because you are using the following constructor (http://www.eel.is/c++draft/any.cons#8):

// Effects: Constructs an object of type any that contains an object of 
// type std::decay_t<T> direct-initialized with std::forward<T>(value).
template< class T>
any( T&& value );

In this case, T is const char(&)[1] (the type of the string literal ""), and thus std::decay_t<const char(&)[1]> will give you const char*, which is why the typeid() of std::any("").type() is the type ID of const char*.

Holt
  • 36,600
  • 7
  • 92
  • 139
  • So what I want is to apply `std::decay` before `typeid`? – val - disappointed in SE Jun 12 '19 at 14:30
  • 2
    @val I don't really know what you want to do (not clear from your question), but if you create an object of type `std::decay_t` from your object of type `T`, this should give you this behavior, similar to `std::any` behavior. – Holt Jun 12 '19 at 14:32
4

Why do results for std::any("").type() and typeid("") differ?

Accroding to the following reference:

template< class ValueType >
any( ValueType&& value );

4) Constructs an object with initial content an object of type std::decay_t<ValueType>, direct-initialized from std::forward<ValueType>(value).

std::decay_t<const char[1]> is const char*.


Here is a quote from FrankHB1989 on the isocpp.org forum, which I think is relevant in understanding std::any, in context of this question:

[std::any] is even not for "any object". As I have argued before, I have expect the word "any" being short for "any first-class object type" (i.e. cv-unqualified non-array object type), but std::any has additional refinement of CopyConstructible on the value type, so it is actually for "any copyable cv-unqualified non-array object type" instead.

As such

Is there a way to get first behavior, i.e. make results for string literals consistent (I use type identification to call different type handlers)?

There is no way of having std::any of array (you can have std::any of std::array, but string literal is not a std::array), and there is no way of making typeid("") be a pointer. However, you can use std::decay_t<decltype("")> to get the same type as stored in std::any.

eerorika
  • 232,697
  • 12
  • 197
  • 326
2

It is a common misconception that a string literal has type const char*.

It doesn't. It has type const char[<size + 1>] (plus one for the null terminator).

e.g. "" has type const char[1].

But we often assign a string literal to a const char*, out of convention (and also because otherwise we trigger special rules that result in copying the string).

Furthermore, array name decay rules actually make it quite hard to observe the array-ness of a name in C (and, by extension, C++); that std::any works the way it does is an example of that.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055