I'm trying to group specializations together to avoid writing them multiple times. For example, in the below code, I try to specialize "float" and "double" as one case of implementation for foo::func(); I then use another implementation for "bool."
template<typename T> struct foo;
template<typename T> struct bar;
template<> struct bar<float> { typedef float Type; };
template<> struct bar<double> { typedef double Type; };
/* specialize for float and double here */
template<typename T> struct foo<typename bar<T>::Type> {
static void func() { ... }
};
template<> struct foo<bool> {
static void func() { ... }
};
This errors out in GCC 4.4.3. (This is a target compiler, because it's stock for Ubuntu Server 10.04 LTS, which allegedly has three more years to live.) The error is:
foo.cpp:8: error: template parameters not used in partial specialization:
foo.cpp:8: error: ‘T’
The error refers to the first specialization of foo (for "float" and "double.")
I don't see what part of C++ I'm violating here -- if anyone knows the chapter and verse, I'd appreciate it. Also, if someone knows of another way of accomplishing the same goal (re-using specializations for certain groups of types, without unnecessarily verbose code,) I'd also appreciate any suggestions!
The act of creating a new definition of a function, class, or member of a class from a template declaration and one or more template arguments is called template instantiation. The definition created from a template instantiation is called a specialization.
Template argument deduction is used in declarations of functions, when deducing the meaning of the auto specifier in the function's return type, from the return statement.
template<typename T> struct foo<typename bar<T>::Type> {
static void func() { ... }
};
You're using T
in non-deducible context, hence the compiler cannot deduce T
even if it knows the value of bar<T>::Type
.
Suppose you write,
foo<double> foodouble;
then you probably think, bar
which is specialized with double
will be selected when instantiating foo
? That seems reasonable only if the compiler can make sure that there doesn't exist another specialization of bar
which defines double
as nested type, something like this:
template<> struct bar<int> { typedef double Type; };
Now bar<double>::Type
and bar<int>::Type
both give double
. So the bottomline is: there could exist infinite number of specialization of bar
, all of which may provide double
as nested type, making it impossible for the compiler to uniquely deduce the template argument for bar
class template.
You can use SFINAE as:
#include <iostream>
template<typename T> struct bar { typedef void type; };
template<> struct bar<float> { typedef bar<float> type; };
template<> struct bar<double> { typedef bar<double> type; };
template<typename T>
struct foo : bar<T>::type
{
static void func()
{
std::cout << "primary template for float and double" << std::endl;
}
};
template<>
struct foo<bool>
{
static void func()
{
std::cout << "specialization for for bool" << std::endl;
}
};
int main()
{
foo<float>::func();
foo<double>::func();
foo<bool>::func();
}
Output (online demo):
primary template for float and double
primary template for float and double
specialization for for bool
Note that struct foo : bar<T>::type
is not a specialization anymore. It is a primary template. Also note that it may not be what you want, as it disables all instantiations of the class template with type argument other than float
, double
and bool
; for example, you cannot use foo<int>
. But then I also note that you've left the primary template undefined, so I hope this solution fits in with your requirement.
I don't think that what you are attempting is possible. I found a similar question here. I don't have a link, but the relevant section of the c++ standard is 14.8.2.5. Here is a quote from the section on the deduction of template arguments:
The non-deduced contexts are:
— The nested-name-specifier of a type that was specified using a qualified-id.
— A non-type template argument or an array bound in which a subexpression references a template
parameter.
— A template parameter used in the parameter type of a function parameter that has a default argument
that is being used in the call for which argument deduction is being done.
— A function parameter for which argument deduction cannot be done because the associated function
argument is a function, or a set of overloaded functions (13.4), and one or more of the following apply:
— more than one function matches the function parameter type (resulting in an ambiguous deduc-
tion), or
— no function matches the function parameter type, or
— the set of functions supplied as an argument contains one or more function templates.
— A function parameter for which the associated argument is an initializer list (8.5.4) but the parameter
does not have std::initializer_list or reference to possibly cv-qualified std::initializer_list
type. [ Example:
template<class T> void g(T);
g({1,2,3});
// error: no argument deduced for T
— end example ]
— A function parameter pack that does not occur at the end of the parameter-declaration-clause.
In your case you are specifying the type using a qualified id, so the argument cannot be deduced.
Without giving it too much thought, as a quick workaround you could add a second non-type bool parameter to foo -- something like:
template<typename T, bool is_decimal = false>
struct foo {...}; // general case
template<typename T>
struct foo<T, true> { ... }; // case where T is float or double
Best of luck...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With