Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is this partial function template specialization?

I came up with this after answering this question

I had a simple function template (C++11):

template<class elem_t, class list_t>
bool in_list(const elem_t& elem, const list_t& list) {
   for (const auto& i : list) {
      if (elem == i) {
         return true;
      }
   }
   return false;
}

But GCC emitted warnings because it doesn't seem to like deducing a template parameter as a std::initializer_list. So, without thinking, I made a specialization:

template<class elem_t>
bool in_list(const elem_t& elem, std::initializer_list<elem_t> list) {
   for (const auto& i : list) {
      if (elem == i) {
         return true;
      }
   }
   return false;
}

This worked. No more warnings. But when I looked again and thought about it, I remembered that C++ does not support partial template specialization on function templates. But that is what this appears to be. My only guess is that this is allowed because std::initializer_list is still dependent upon the template parameter, so it is, in essence, a different template. But I'm not sure if this is how it is supposed to be (isn't there a gotw about templates not overloading?).

Is it standard behavior to accept this? And why?

And as a bonus question, why does GCC not like deducing a template parameter as a std::initializer_list? It seems quite silly to expect me to copy and paste the code and just replace the parameter with a std::initializer_list.

The warning message:

test.cpp: In function ‘int main()’:
test.cpp:33:43: warning: deducing ‘const list_t’ as ‘const std::initializer_list<int>’ [enabled by default]
test.cpp:6:6: warning:   in call to ‘bool in_list(const elem_t&, const list_t&) [with elem_t = int, list_t = std::initializer_list<int>]’ [enabled by default]
test.cpp:33:43: warning:   (you can disable this with -fno-deduce-init-list) [enabled by default]

When called by in_list(3, {1, 2, 3, 4, 5});

EDIT: Apparently deducing a template parameter as an initializer_list is an extension according to the working draft for my version of GCC (cite). So new question: Is this still an extension as of the final c++11 standard? If so, this would mean that it would be necessary for me to add the second function for standards-compliant code. Thanks for all your help!

EDIT2: The compiler dialect flag appears to be removed for GCC 4.7, so it seems like the issue was resolved, but I don't know how it was resolved.

like image 771
Robert Mason Avatar asked Jun 23 '26 08:06

Robert Mason


2 Answers

Using what @Ben Voigt said in the comments on the other answer, I have gathered some relevant standard quotes:

§14.5.6.2

A function template can be overloaded with other function templates and with normal (non-template) functions. A normal function is not related to a function template (i.e., it is never considered to be a specialization), even if it has the same name and type as a potentially generated function template specialization.

So that rules out function template specialisation as what you're doing, because even if two function template overloads could potentially generate the same function, it's not specialisation. So it's overloading.

Such specializations are distinct functions and do not violate the one definition rule (3.2).

So they're distinct functions and that's why it's not erroring.

§14.5.6.2.1

If a function template is overloaded, the use of a function template specialization* might be ambiguous because template argument deduction (14.8.2) may associate the function template specialization with more than one function template declaration.

This is priming is for what we both already saw, which is that in_list(a, b) where b is an initializer_list appears to match both function templates.

(*Note that "function template specialization" here doesn't mean specialising a function template, it means a function template that has been instantiated with a type. So with template<typename T> f();, f<int>() is a function template specialisation.)

So we use what is called partial ordering of overloaded function templates to resolve this:

Partial ordering of overloaded function template declarations is used in the following contexts to select the function template to which a function template specialization refers:

— during overload resolution for a call to a function template specialization (13.3.3);

— when the address of a function template specialization is taken;

— when a placement operator delete that is a function template specialization is selected to match a placement operator new (3.7.4.2, 5.3.4);

— when a friend function declaration (14.5.4), an explicit instantiation (14.7.2) or an explicit specialization (14.7.3) refers to a function template specialization.

Ok, so that's when partial ordering is for. This is what it does:

Partial ordering selects which of two function templates is more specialized than the other by transforming each template in turn (see next paragraph) and performing template argument deduction using the function type. The deduction process determines whether one of the templates is more specialized than the other. If so, the more specialized template is the one chosen by the partial ordering process.

And then you get into the long and laborious process of determining which template is more specialised, which you can read about if you want, but it's really complicated and I probably don't understand it all (and plus, I don't have enough time to write about it :)).

like image 136
Seth Carnegie Avatar answered Jun 24 '26 22:06

Seth Carnegie


That's not partial specilization. What you are doing is overloading function.

like image 42
dchhetri Avatar answered Jun 24 '26 22:06

dchhetri



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!