Question: It is a bit strange. I am trying to write a code which detects whether a type is a string object. The char \* or other object types should lead to false not true. The following code gives me: 

> 
> error: template parameters not deducible in partial specialization:
> 
> 
> 

I do not understand what the message means. Even searched online, I cannot figure out how to fix this:

```
#include <iostream>
#include <type_traits>
#include <string>

template <typename T>
struct is_string : std::false_type { };

template <typename T>
struct is_string<std::string> : std::true_type<T> { };

class Temp
{
  int a;
};

int main()
{
    // Expect: false
    std::cout << is_string<int>::value << std::endl;
    std::cout << is_string<char *>::value << std::endl;
    std::cout << is_string<Temp>::value << std::endl;

    // Expect: true
    std::cout << is_string<std::string>::value << std::endl;
    std::cout << is_string<const std::string>::value << std::endl;
    std::cout << is_string<std::string&>::value << std::endl;
}

```

If there is a out of box `std` tool, that's welcome

Answer: 
The template specialization should look like this:

```
template <>
struct is_string<std::string> : std::true_type { };

```

But even if you use it, your template will return `false` for cv-qualified `string` or references to it.

> 
> I did not use `std::is_same` because it rejects types such as `const string` or `string &` as string.
> 
> 
> 

The right way to do it is `std::is_same_v<std::remove_cv_t<std::remove_reference_t<`**`your_type`**`>>, std::string>` (or using `std::decay_t`, as the other answer suggests).

Or, in C++20: `std::is_same_v<std::remove_cvref_t<<`**`your_type`**`>, std::string>`.
When you create a specialization, you can kinda think of the template parameters of the specialization as "variables" for the specialization. You can give as many as you want, but they must be used in the angle brackets that are part of the class.

When you create a full specialization, you do not provide any template parameters - empty angle brackets.

The other answers by HolyBlackCat and Jarod42 are correct, in that they get rid of your compiler error, and provide a working implementation for your vague specifications.

However, both of them suggest doing more than removing cv-qualifiers from the type. I do not recommend doing this, as it goes against how the type traits in the standard library work and it could be quite surprising to users.

Since you actually want that as a result, I would suggest providing a separate metafunction that uses the basic one, and make it clear that it is removing the reference.

```cpp
static_assert(std::is_null_pointer<std::nullptr_t>::value);
static_assert(std::is_null_pointer<std::nullptr_t const volatile>::value);
static_assert(not std::is_null_pointer<std::nullptr_t &>::value);

static_assert(std::is_unsigned<unsigned long>::value);
static_assert(std::is_unsigned<unsigned long const volatile>::value);
static_assert(not std::is_unsigned<unsigned long &>::value);

```

Plus, `std::string` is just one of the possible standard string types. What about a string using a different character type (e.g., `std::wstring`), or the same character type but a different allocator (e.g., `std::pmr::string`)?

Those already show how to limit your trait for just `std::string`. Here is one way to do it for any standard string type.

```cpp
namespace detail {
template <typename T>
struct is_string
: std::false_type
{};

// Partial specialization - parameters used to qualify the specialization
template <typename CharT, typename TraitsT, typename AllocT>
struct is_string<std::basic_string<CharT, TraitsT, AllocT>>
: std::true_type
{};
}

template <typename T>
using is_basic_string = detail::is_string<std::remove_cv_t<T>>;

static_assert(is_basic_string<std::string>::value);
static_assert(is_basic_string<std::string const volatile>::value);
static_assert(not is_basic_string<std::string &>::value);
static_assert(is_basic_string<std::wstring>::value);
static_assert(is_basic_string<std::pmr::string>::value);

```