- ⚠️ Comparing
const chararrays with==in templates compares pointers, not content. - 🧠 C++ array-to-pointer decay removes size information, even when deduced in a template.
- 💡 Use
std::string_view,std::array, orstd::strcmpfor content-based comparisons. - ⚙️ Static assertions with
static_assertcan enforce compile-time safety in templates. - 🔍 Common in test utilities and embedded systems, incorrect array comparisons can silently fail.
In C++, comparing const char arrays inside templates leads to silent pointer-based behavior due to array-to-pointer decay. This can cause unexpected outcomes when sizes differ or content matches but pointers don’t. This article shows how and why this happens, what it means, and how to use safer ways to write C++ code that works more predictably and is easier to keep up with.
1. What is a const char[] Array in C++?
In C++, a const char[] is a character array with a fixed size. You cannot change its contents. These arrays usually hold string literals or data you don’t want to change. For example:
const char msg[] = "hello";
Here, msg is an array with 6 characters ('h', 'e', 'l', 'l', 'o', '\0'). A const char[] knows its length when the code is compiled, unlike a const char*, which is just a pointer to the first character. This helps when you pass it to template functions. The function can figure out the size and use it while the code compiles. But this useful size information goes away if array-to-pointer decay happens.
This small difference between const char[] and const char* matters a lot in templates. The array’s size links to template parameters, like N in const char (&arr)[N]. But this also makes it easy to compare arrays in ways that are misunderstood and cause errors.
2. The Role of Array-to-Pointer Decay in C++
Array-to-pointer decay is a basic rule in C++: when you use an array in most expressions or give it to a function, it automatically changes into a pointer to its first item. This behavior usually happens without you noticing and works well with how flexible C++ is. But it means you lose some type safety, especially in templates.
For example:
void print(const char* str);
const char arr[] = "hi";
print(arr); // Here, arr, which is const char[3], changes to const char*
When the array changes, you lose its size. The original code said it was an array type, but inside the print() function, it’s just a stream of characters ending with \0. More important, if this happens inside a template, the function might do things with pointers that act differently than if it knew the array’s size.
Templates give you a chance to get the array size when figuring out the type. But that help is lost if you do anything that makes the array change into a pointer.
3. Comparing Arrays in C++: Undefined or Undesired?
C++ does not let you compare the content of arrays using operator==. If you use == to compare arrays, you are comparing their addresses. This means you are checking if two arrays are the exact same object in memory:
const char a[] = "abc";
const char b[] = "abc";
bool result = (a == b); // False, unless the compiler puts identical strings in the same spot
This is how C++ handles arrays: there is no built-in way to compare what’s inside them. This is different from languages where comparing arrays (like Python lists or Java arrays) checks their content unless you set it up otherwise.
In C++, the compiler will not see that you want to check if the content is the same, unless you write code that compares each character. This is risky in general code and template functions because developers might think the comparison checks the content.
4. What Happens During a Const Char Array Comparison in a Template?
Code written with templates can get arrays as references, and their lengths are figured out:
template<typename T, std::size_t N, std::size_t M>
bool compare(const T (&a)[N], const T (&b)[M]) {
return a == b; // This is a risky assumption!
}
The compiler correctly sees that a is const T[4] and b is const T[6]. But the == operator used inside the function makes them change into pointers. Both a and b become const T*. So the comparison checks pointers, not arrays or their contents.
This happens because C++ does not have operator== set up to work for array types. You might have thought the compiler would notice the sizes don’t match, especially since it knows N and M. But checking pointers is correct by type rules no matter the size, so the code compiles without any error.
5. Example That Sparks the Confusion
Think about this code snippet that seems okay at first:
template<std::size_t N, std::size_t M>
bool compare(const char (&a)[N], const char (&b)[M]) {
return a == b;
}
int main() {
compare("foo", "bar"); // Compiles just fine
}
Here, "foo" and "bar" are const char[4] because of the null character (\0) at the end. The compiler figures out that N = 4 and M = 4. Still, this comparison does not do what a developer probably wants.
Why? Because return a == b; checks pointers (const char* == const char*). It compares their addresses in memory. "foo" and "bar" are two different pieces of text data, kept in different memory spots. Even if their content is the same, the check returns false.
Now picture what happens if you use "foo" and "foo":
compare("foo", "foo"); // Might return true
This might return true. This is not because the strings are the same. It is because the compiler might save memory by using the same spot for identical string data. But this can be different depending on the compiler. Relying on it is risky and might not work everywhere.
6. No Compile-Time Error – Why?
The compiler sees everything in your templated function as correct C++ code:
- The arrays change into pointers when you use them in the comparison.
const char* == const char*is a correct way to compare pointers.- The compiler thinks you want to compare addresses, so it does not give a warning or error.
- Different sizes (
N != M) do not matter for thea == bcheck because the array sizes only affect the template’s description.
This makes sense according to C++’s rules. But for anyone expecting strong type safety or correct behavior when the code runs, this is a common mistake to make.
7. The False Sense of Type Safety
It’s easy to feel a false sense of safety because template parameters seem to protect against bugs:
template<std::size_t N, std::size_t M>
void checkArrays(const char (&a)[N], const char (&b)[M]) {
static_assert(N == M, "Array sizes must match");
// Do something
}
This check will stop arrays of different sizes from compiling. But most developers do not include checks like this. They just want to compare content and wrongly think a == b is enough.
Without the static_assert, nothing stops the wrong behavior, and the compiler says the code is fine.
8. How C++ Template Type Deduction Works in This Context
In C++, templates can figure out the size of an array by looking at its type with a reference:
template<typename T, std::size_t N>
void printSize(const T (&arr)[N]) {
std::cout << "Array Size: " << N << '\n';
}
Here, the array size N is found and is known completely when the code compiles. This is useful because you can check things about the array during compilation. But inside the function, if you write arr + 1, for example, the array immediately stops being an array and becomes a pointer calculation of type const T*.
This situation—keeping the size in the function’s description but changing to a pointer in the code—is why array template functions can act in ways people don’t expect.
9. Safer Alternatives to Comparing Const Char Arrays
To make sure you compare content and get the correct result, think about using other options:
🧵 Using std::string_view (Modern Safe Way)
template<std::size_t N, std::size_t M>
bool compare(const char (&a)[N], const char (&b)[M]) {
return std::string_view(a) == std::string_view(b);
}
Why it’s better:
- It keeps size information.
- It compares content.
- It is fast because it does not make full copies of the data.
🧪 Use std::strcmp() (Simple and Safe for C-style strings)
template<std::size_t N, std::size_t M>
bool compare(const char (&a)[N], const char (&b)[M]) {
return std::strcmp(a, b) == 0;
}
What it needs:
- The strings must end with a null character.
- This way is a bit old in modern C++.
📦 Use std::array<char, N> for Type Safety
template<std::size_t N, std::size_t M>
bool compare(const std::array<char, N>& a, const std::array<char, M>& b) {
if constexpr (N != M) return false;
return std::equal(a.begin(), a.end(), b.begin());
}
Good points:
- Keeps size info when compiling.
- Compares content, types are strong.
- Works well with
constexprand general code.
10. How to Generate Compile-Time Errors for Mismatched Sizes
Making sure array sizes are equal helps code work better. Use static_assert for this:
template<std::size_t N, std::size_t M>
bool compare(const char (&a)[N], const char (&b)[M]) {
static_assert(N == M, "Mismatched array sizes");
return std::equal(a, a + N, b);
}
You can add more checks during compilation using things like:
std::enable_ifstd::conjunctionfrom<type_traits>- C++20
requires,concepts
Example using requires in C++20:
template<std::size_t N, std::size_t M>
requires (N == M)
bool compare(const char (&a)[N], const char (&b)[M]) {
return std::equal(a, a + N, b);
}
This stops the template from even being set up if you pass arrays of different sizes. Problems are found right away.
11. Boosting Developer Confidence with a Mental Model
Developers can avoid costly bugs by learning a few main ways to think about this:
- Arrays change into pointers in code, but not when you just say what type they are.
- Template parameters can hold size information—use it.
- Comparing with
==does not make sense unless you want to compare memory addresses. - Use tools that compare content by default:
std::string_view,std::strcmp,std::equal.
By making choices based on these ideas, you will write C++ code that works better and shows what you meant to do.
12. Beyond the Syntax: What it Means for Large Programs
In big company software or embedded systems, these kinds of bugs might not show up in small tests. They might only appear when everything is put together or when the software is running. This can cost a lot.
If your template code expects data to be equal but you are just checking addresses, you might get:
- Wrong test results in testing tools.
- Security problems because text data is not checked correctly.
- Crashes because of wrong guesses about memory.
Make it a rule to use the right tools for comparisons. Also, use automated checks with static analysis tools or checks in your build process.
13. Use Cases in Libraries or Frameworks That Might Lead to Mistakes
Template-based libraries for testing, getting info about types, or writing code that writes code are likely to have these issues. Examples include:
- Tools in unit tests that check strings.
- Embedded systems that compare chunks of data.
- Tools that look into code structure but do not check sizes carefully even when they know the type.
Libraries like Boost, Google Test, and embedded C libraries that often use raw arrays should have strong checks for types and sizes. This helps prevent these small but risky problems.
14. Key Takeaways
- Comparing
const char[]in C++ templates checks pointers by default because of array decay. - Code with arrays of different sizes will still compile unless you add specific checks.
- Use
std::string_view,std::array, or functions that compare string content to make your code stronger. - Checks that run when compiling, like
static_assertor concepts, make templates more dependable. - Learn and respect array-to-pointer decay—do not think strong type checking will always mean the behavior is correct.
Do not let bugs hide quietly. Make your code’s writing match what it is supposed to do. Let your templates show what you mean, not just what the types are.
Want to avoid template surprises like these? Read our guide on Understanding Array Decay in C++ or learn more about compile-time validation patterns with SFINAE and compile-time validation patterns.
Citations
- https://en.cppreference.com/w/cpp/language/array
- https://isocpp.org/wiki/faq/pointers-to-members#array-vs-ptr
- https://stackoverflow.com/questions/5093460/how-do-i-pass-a-length-specified-array-to-a-function-in-c
- https://abseil.io/tips/91