Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++20 Concepts : Which template specialization gets chosen when the template argument qualifies for multiple concepts?

Tags:

Given :

#include <concepts> #include <iostream>  template<class T> struct wrapper;  template<std::signed_integral T> struct wrapper<T> {     wrapper() = default;     void print()     {         std::cout << "signed_integral" << std::endl;     } };  template<std::integral T> struct wrapper<T> {     wrapper() = default;     void print()     {         std::cout << "integral" << std::endl;     } };  int main() {     wrapper<int> w;     w.print(); // Output : signed_integral     return 0; } 

From code above, int qualifies to both std::integral and std::signed_integral concept.

Surprisingly this compiles and prints "signed_integral" on both GCC and MSVC compilers. I was expecting it to fail with an error along the lines of "template specialization already been defined".

Okay, that's legal, fair enough, but why was std::signed_integral chosen instead of std::integral? Is there any rules defined in the standard with what template specialization gets chosen when multiple concepts qualify for the template argument?

like image 342
Lewis Liman Avatar asked Jan 28 '20 16:01

Lewis Liman


People also ask

How do you use template arguments in C++?

A template argument for a template template parameter is the name of a class template. When the compiler tries to find a template to match the template template argument, it only considers primary class templates. (A primary template is the template that is being specialized.)

What is the specialty of a template function give example?

Template in C++is a feature. We write code once and use it for any data type including user defined data types. For example, sort() can be written and used to sort any data type items. A class stack can be created that can be used as a stack of any data type.

Which is the correct example of template parameters?

For example, given a specialization Stack<int>, “int” is a template argument. Instantiation: This is when the compiler generates a regular class, method, or function by substituting each of the template's parameters with a concrete type.

What are template arguments enlist types of template arguments?

In C++ this can be achieved using template parameters. A template parameter is a special kind of parameter that can be used to pass a type as argument: just like regular function parameters can be used to pass values to a function, template parameters allow to pass also types to a function.


1 Answers

This is because concepts can be more specialized than others, a bit like how template order themselves. This is called partial ordering of constraints

In the case of concepts, they subsumes each other when they include equivalent constraints. For example, here's how std::integral and std::signed_integral are implemented:

template<typename T> concept integral = std::is_integral_v<T>;  template<typename T> //   v--------------v---- Using the contraint defined above concept signed_integral = std::integral<T> && std::is_signed_v<T>; 

Normalizing the constraints the compiler boil down the contraint expression to this:

template<typename T> concept integral = std::is_integral_v<T>;  template<typename T> concept signed_integral = std::is_integral_v<T> && std::is_signed_v<T>; 

In this example, signed_integral implies integral completely. So in a sense, a signed integral is "more constrained" than an integral.

The standard writes it like this:

From [temp.func.order]/2 (emphasis mine):

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. If both deductions succeed, the partial ordering selects the more constrained template as described by the rules in [temp.constr.order].

That means that if there is multiple possible substitution for a template and both are choosen from partial ordering, it will select the most constrained template.

From [temp.constr.order]/1:

A constraint P subsumes a constraint Q if and only if, for every disjunctive clause Pi in the disjunctive normal form of P, Pi subsumes every conjunctive clause Qj in the conjunctive normal form of Q, where

  • a disjunctive clause Pi subsumes a conjunctive clause Qj if and only if there exists an atomic constraint Pia in Pi for which there exists an atomic constraint Qjb in Qj such that Pia subsumes Qjb, and

  • an atomic constraint A subsumes another atomic constraint B if and only if A and B are identical using the rules described in [temp.constr.atomic].

This describe the subsumption algorithm that compiler use to order constraints, and therefore concepts.

like image 116
Guillaume Racicot Avatar answered Oct 15 '22 17:10

Guillaume Racicot