C ++ function template specialization declarations and template arguments; none vs. <> vs. <type>

When looking at function templates, I see specializations declared in different ways:

template<> void f(argtype) {}
template<> void f<>(argtype) {}
template<> void f<argtype>(argtype) {}

      

... and I'm wondering about the differences between the two. Given the example below with template functions with and without parameter, I have a few questions.

#include <iostream>
#include <typeinfo>

//Function print1 WITH function parameter---------------------------------------------
template<class T>
void print1(T) { std::cout << "Primary template for print1() with type " << typeid(T).name() <<  std::endl; }

template<>
void print1<int>(int) { std::cout << "Specialization for print1<int>(int)" << std::endl; }

//Not allowed, deduced to be the same as print1<int>(int)
/*template<>
void print1<>(int) { std::cout << "Specialization for print1<>(int)" << std::endl; }*/

//Not allowed, deduced to be the same as print1<int>(int)
/*template<>
void print1(int) { std::cout << "Specialization for print1(int)" << std::endl; }*/

//Function print2 WITHOUT function parameter------------------------------------------
/*Not allowed together with print<>(); compiler complains: 
    t2.cpp:29:6: error: template-id 'print2<>' for 'void print2()' does not match any template declaration*/
/*template<class T>
void print2() { std::cout << "Primary template for print2()" << std::endl; }*/

template<class T = short> //Declaration of print2<>() now ok in conjunction with print2<>()
void print2() { std::cout << "Primary template for print2()" << std::endl; }

template<>
void print2<int>() { std::cout << "Specialization for print2<int>()" << std::endl; }

template<>
void print2<>() { std::cout << "Specialization for print2<>()" << std::endl; }

int main() {
    //These three work in the same way, no matter which call method we use, so far so good
    print1(10);
    print1<>(10);
    print1<int>(10);
    print1(true);
    print1<>(true);
    print1<bool>(true);

    print2(); //Triggers print2<>(), a bit unexpectedly, should trigger print2<short>() (primary template)
    print2<>(); //Triggers print2<>(), a bit unexpectedly, should trigger print2<short>() (primary template)
    print2<bool>(); //Triggers print2<bool>() primary template
    print2<short>(); //Triggers print2<>(), should definately trigger primary template for print2()
    print2<int>(); //Triggers print2<int>() specialization
    return 0;
}

      

outputs:

Specialization for print1<int>(int)
Specialization for print1<int>(int)
Specialization for print1<int>(int)
Primary template for print1() with type b
Primary template for print1() with type b
Primary template for print1() with type b
Specialization for print2<>()
Specialization for print2<>()
Primary template for print2()
Specialization for print2<>()
Specialization for print2<int>()

      

  • What special meaning follows from the output of a template specialization argument as empty, nonexistent, or of a specialized type, and how does it affect the result? It seems that with a function argument, this specification is overkill and the compiler will infer it regardless of how it is specified (as a result, equivalent explicit specifications become unreasonable over-estimates).
  • I understand that given a function without parameters, the special template argument needs to be explicitly specified in the declaration to indicate which instance a particular function refers to (since it cannot be inferred otherwise). But the meaning in this case implies something more, and "empty" specialization (<>) is triggered in several unexpected ways. Why?
  • Why should I have a default template parameter when specializing print2 with print2 <> (), but not without it?
+3


source share


2 answers


What is the special meaning of exiting a template specialization argument empty, nonexistent, or with a specialized type, and how does it affect the result?

If you've provided a complete template argument list, you are just explicitly specializing the function template for a given set of template arguments.

If you provide arguments for a (possibly empty) subset of template parameters, then you are explicitly specializing the function template for the set of arguments to be inferred . According to [temp.deduct.decl]:

In a declaration whose id declarator refers to the specialization function template, template argument deduction is performed to determine the specialization to which the declaration belongs. In particular, this is done for explicit instances (14.7.2), explicit specializations (14.7.3), and some friend declarations (14.5.4). [...]. In all these cases, it P

is the type of the function, the template is considered a potential match, and the A

[...] type of the function from the declaration [...].
The deduction is made as described in 14.8.2.5.

If for the set of function templates in question there is either no match or more than one match after a partial order (14.5.6.2), the deduction ends and, in cases of declaration, the program is poorly formed.

So, for each parameter for which no argument is specified, or in the case when the list is not specified at all, the template argument is deduced for each corresponding parameter of the specialization and its analog from the primary template. The process is described in 14.8.2.5 and works the same as if we named the primary template with the supplied template argument list as template arguments and parameter type objects in the specialization as function arguments.

You should be familiar with the fact that it is possible to call a function template with some specified template arguments, for example

template <typename A, typename B> void foo(A, B);

foo(7684, 48.);
foo<int>(7684, 48.);
foo<int, double>(7684, 48.);

      

This is equivalent for explicit specializations:

template <typename T, typename U>
void foo(T, U) {}

// Both template arguments have to be deduced.
template<> void foo(double, float);

// The *exact* same as above.
// template<> void foo<>(double, float);

// Second template argument has to be deduced by type.
// If we call foo<int>(int(), float()) then the deduced specialization is
// foo<int, float>, thus U=float.
template<> void foo<int>(int, float);

template<> void foo<int, int>(int, int);

      

This can also be applied to function template overloads. In an attempt to find the primary template corresponding to the specialization, the most specialized one is selected.

template <typename T, typename U>
void foo(T&, U&) {}

template <typename T, typename U>
void foo(T const&, U&) {}

// Specializes the second overload because it is more specialized.
template <>
void foo(int const&, float&);

      



Note that when searching for a primary template, the supplied arguments (i.e., not output) are used to validate the resulting functional parameter of the primary template against the resulting functional parameter of specialization. They must be equal.

template <typename T, typename U>
void foo(T&, U&) {}

// Error - no matching primary template found.
template <>
void foo<int, int>(float&, int&);

// Dito:
template <>
void foo<int>(int, int&);

      


It seems that with a function argument this specification is overkill and the compiler outputs it regardless of how it is specified (as a result, equivalent explicit specifications become unacceptable overvalues).

Yes this is true. Note that if you mis-specify a template argument that results in an error:

But the meaning seems to imply something more in this case, and the "empty" specialization (<>) runs in several unexpected ways. Why?

For the call, the template arguments are printed first. Then the specialization is called with these template arguments.

If you have explicitly specialized a function template for that particular specialization, here print2<>

that is print2<short>

, then this explicit specialization is called.
How is it unforeseen?

Why should I have a default template parameter when specializing print2

with print2<>()

, but not without?

Because the compiler cannot deduce the argument. If you provide a default argument, it shouldn't output it in the first place.

+3


source


What special meaning derives from the deduction of template specialization argument empty

Missing arguments are printed whenever possible; an empty argument list means that all arguments should be printed.

does not exist

This means that you are declaring a primary template, not an explicit specialization.

or with a special type



This means that you are declaring an explicit specialization for that type.

an "empty" specialization (<>) is triggered in several unexpected ways. Why?

In both cases, the template argument is output. The print1

main template declares that T

is the same type as the function parameter; so it is derived from the function parameter. In print2

it is declared with the default type short

, so it is used. So your surprise to see print2<>

when you think it should be print2<short>

is explained: print2<>

is print2<short>

.

Why should I have a default template parameter when specializing print2 with print2 <> (), but not without it?

If there was no default argument or function parameters from which to infer an argument, then it would be impossible to infer the type for specialization, so <>

it cannot be used. I don't know what you mean by "without him"; if you mean no <>

, then you are declaring a primary template, not a specialization, and the parameter is generic.

+2


source







All Articles