Click here to Skip to main content
15,868,141 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I'm trying to understand template specialization in modern C++. The code makes no sense, it is only a proof of concept. (Or rather, a disproof until now.)
C++
template<char... cs> struct xyz
{
    static size_t const data = 0;
};

template<char c0, char c1, char c2, char... cs> struct xyz /* [1] */
{
    static size_t const data = 1;
}; /* [2] */

auto x1 = xyz<'a'>::data;
auto x2 = xyz<'a', 'b', 'c', 'd'>::data;

The idea is to have a template that takes any number of characters, and to specialize it with a template that takes at least three characters. I would expect x1==1, and x2==2.

When I compile this in MSVC2022, I get the following error messages at the line marked with [2]:
error C3855: 'xyz': template parameter 'cs' is incompatible with the declaration
error C2977: 'xyz': too many template arguments
message : see declaration of 'xyz'

The third message points to line [1].

In contrast, when I do the same with a function template, results are different:
C++
template<char... cs>size_t zyx() /* [11] */
{
    return 0;
}

template<char c0, char c1, char c2, char... cs> size_t zyx() /* [12] */
{
    return 1;
};

auto z1 = zyx<'a'>();
auto z2 = zyx<'a', 'b', 'c', 'd'>(); /* [13] */

Again, I would assume z1==1, and z2==2.
This time, the template definitions compile error-free, but when I try to use the function, the error messages are:
error C2668: 'zyx': ambiguous call to overloaded function
message : could be 'size_t zyx<97,98,99,100>(void)'
message : or       'size_t zyx<97,98,99,100>(void)'
message : while trying to match the argument list '()'

These messages point to the lines [13], [12], [11], and [13] again.

What I have tried:

I tried with "/std:c++latest" as well as with "/std:c++20", with identical results.

Have I run into a compiler bug, or is it my wrong thinking?

Edit: When I use compiler explorer, it fails with every compiler I try. I conclude it must be the code, not the compiler.
Posted
Updated 24-Dec-22 6:46am
v2
Comments
0x01AA 24-Dec-22 10:45am    
Let's hope @code-witch does read this question. I'm also keen on the answer ;)

Your code initially looked fine to me and then I saw it.

You can't specialize by number of template arguments in C++ because you must have parameters after struct xyz, such as struct xyz<...ch> as specializations require some of the template arguments to be provided.

There's a more complicated way to do it I think. After all, this is C++, and one can do anything if they don't care what the resulting code looks like. :~

just take the cs parameter pack. Then use sizeof...(cs) to get the parameter pack count, then forward that to a template that also takes the size as an argument, and then you can specialize there.

Maybe something like this (I haven't tested it but i think it will work):

C++
template<size_t count,char... cs> struct xyz2 {
    static size_t const data = 0;
};
template<char... cs> struct xyz2<4,cs...> {
    static size_t const data = 1;
};
template<char... cs> struct xyz
{
    static size_t const data = xyz2<sizeof...(cs),cs...>::data;
};

auto x1 = xyz<'a'>::data;
auto x2 = xyz<'a', 'b', 'c', 'd'>::data;


I should add that while the title says C++20, this solution will work with C++11 and on.
 
Share this answer
 
v3
Comments
0x01AA 24-Dec-22 11:17am    
Thank you so much and of course my small 5 ;)
honey the codewitch 24-Dec-22 11:18am    
No problem at all. Glad to help. I love little C++ brain teasers =)
0x01AA 24-Dec-22 11:32am    
And sorry for my unconventional way of trigger. You are very great!
honey the codewitch 24-Dec-22 21:25pm    
Hey, whatever works
hans.sch 24-Dec-22 13:40pm    
Thank you for the explanation! "can't specialize by number of template arguments" did surprise me. With your trick I can continue to explore templates. Just found that with a "requires" clause you can even specialize on the value of the template arguments :-) But I'll leave that for a later post.
Try
C++
template<char... Chars> struct xyz
{
  static size_t const data = 1;
};

template<char c0, char c1> struct xyz<c0,c1>
{
  static size_t const data = 0;
};

template<char c0> struct xyz<c0>
{
  static size_t const data = 0;
};

auto x1 = xyz<'a'>::data;
auto x2 = xyz<'a','b'>::data;
auto x4 = xyz<'a','b','c','d'>::data;
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900