Search code examples
c++c++11templatesstring-literalsperfect-forwarding

Template Argument Deduction from String Literal


I am currently thinking about how to best constrain a generic type of a template to an std::sting as well as string literals. Therefore I compare the deduced type with the desired type using std::is_same. In case of an std::string this works right away. For a string literal, meaning a char const array, it only works if I use std::decay on the type and then compare the result to the type char const *. If I directly compare the deduced type to what I think it should be, is_same returns false, as is illustrated by the following example code.

template <class TYPE>
void function(TYPE&& parameter)
{
  //this doesn't work as expected
  std::cout << typeid(TYPE).name() << " : " << typeid(char const [5]).name() << std::endl;
  std::cout << std::is_same<char const [5], TYPE>::value << std::endl;
  //this works as expected
  std::cout << typeid(std::decay_t<TYPE>).name() << " : " << typeid(char const *).name() << std::endl;
  std::cout << std::is_same<char const *, std::decay_t<TYPE>>::value << std::endl;
}

int main(int argc, char** argv)
{
  function("name");
  return 0;
}

The output generated is the following:

char const [5] : char const [5]
0
char const * __ptr64 : char const * __ptr64
1

Now, what I am wondering is why is_same returns false in the first case even though the types appear to be identical.

The only possible explanation that I could come up with is that within the function std::is_same a transformation similar to std::decay is applied to the type (for instance a function call). But then again this transformation would also occur to the other type, yielding the same result and thus resulting in equality.


Solution

  • String literals are not passed by value as char const [N], but by reference as char const (&)[N].

    This works correctly for me:

    std::cout << std::is_same<char const (&)[5], TYPE>::value << std::endl;
    

    Note here that

    1) Refers to a std::type_info object representing the type type. If type is a reference type, the result refers to a std::type_info object representing the referenced type.

    You can easily verify that is_same doesn't discard reference-ness in the same way as type_info, for example by checking that

    std::is_same<int&, int>::value == false
    

    This explains why the typeid name is the same, but your is_same test still fails.