Constant template arguments for functions - c++

I have the defined the following types:
template <typename TType>
struct symbol{};
template <typename TAtom, unsigned IDim>
struct tensor_type {};
template <unsigned IDim>
using real = tensor_type<double, IDim>;
template <unsigned IDim>
using index = tensor_type<int, IDim>;
I now want to call functions with instances of the type symbol like this:
template <template<typename TType, unsigned IDim> typename TTemplate, typename TType, unsigned IDim>
void f(symbol<TTemplate<TType, IDim>>* sym) {
std::cout << "higher dimension" << std::endl;
}
int main() {
symbol<real<0>>* p1 = new symbol<real<0>>();
symbol<index<0>>* p2 = new symbol<index<0>>();
symbol<real<1>>* p3 = new symbol<real<1>>();
symbol<index<5>>* p4 = new symbol<index<5>>();
f(p1); //dimension 0
f(p2); //dimension 0
f(p3); //higher dimension
f(p4); //higher dimension
}
This works fine and well. But now I want to differ between symbols of the dimension 0 and all the other dimensions, but I do not know how to properly declare an overloaded function properly with the templates to achive this.
I have tried the following:
template <typename TType>
void f(symbol<TType<0>>* sym) {
std::cout << "dimension 0" << std::endl;
}
with error: ‘TType’ is not a template during compilation and
template <template<typename TType> typename TTemplate, typename TType>
void f(symbol<TTemplate<TType>>* sym) {
std::cout << "dimension 0" << std::endl;
}
with error: wrong number of template arguments (2, should be 1).
I cannot include the code for both dimensions in one function because in the actual implementation types with dimension 0 have different attributes than the one with higher dimensions, so even if I use a case distinction in the function the compiler complains that some passed arguments do not have the needed attribute.
One possiblity is to declare a function for every possible type for dimension 0 like this
void f(symbol<real<0>>* sym) {
std::cout << "dimension 0" << std::endl;
}
but since I have multiple types in the actual implementation and all the functions would look exactly the same (besides the signature) I wanted to avoid redundant code.
Is there some way to achive this more elegant?

In your shown example, there is only one relevant class template: tensor_type
The others declared with using are just aliases for this one. They do not define new class templates or types.
So with that it is enough to simply specify that template in the overload:
template <typename T>
void f(parameter_symbol<tensor_type<T, 0>>* sym) {
std::cout << "dimension 0" << std::endl;
}
If you have multiple templates, some of which you haven't shown, then
template <template<typename, unsigned> typename TTemplate, typename T>
void f(parameter_symbol<TTemplate<T, 0>>* sym) {
std::cout << "dimension 0" << std::endl;
}
will work. Note that the kinds of template parameter in the template template parameter template<typename, unsigned> typename TTemplate must match the class template that you are going to use as argument. (As one exception, you can also replace unsigned with auto.)
If you have multiple templates with different kinds of template parameters, then you need define such overloads for all cases.
If the dimension is not always in the same position in the parameter list, then this approach doesn't work.
It would be simpler to have the relevant classes provide a constexpr static member variable idim which indicates the dimension, or a constexpr static function returning it. Or alternatively a specialized free variable template or function template.
Then you can have a function overload taking any type and constrain it through SFINAE or if you can use C++20, a requires class:
template <typename T>
requires(T::idim == 0)
void f(parameter_symbol<T>* sym) {
std::cout << "dimension 0" << std::endl;
}

Related

General function for types and const types without overloading

I know it's generally a bad idea to rely on SFINAE unless absolutely necessary, but I'm curious about how to do the following anyway.
So let's say I have a function that prints a type to the console (class used for partial specialization since it more closely matches my situation):
template <class Ty>
class print
{
public:
print(Ty line)
{
std::cout << line << std::endl;
}
};
Since this is scaled down code, I'm not sure if it would also work with const types, but (because does not in my specific case) let's say the above function does not work with const types. Correct me if I'm wrong, but I believe this would be how you'd accomplish this with partial template specialization?
template <class Ty>
class print <const Ty>
{
public:
print(const Ty line)
{
std::cout << line << std::endl;
}
};
However, is there a way to use the <type_traits> header to do this? I've come across a question that was specific to char* and const char*, but it seems to be different when generalized. Additionally, that question (and answer) is nearly 7 years old.
I've tried the following code (untested) when trying to adapt the answer from the above question to my own situation, but it seems like there should simply be a better way to accomplish this task. In fact, I'm pretty sure my code won't compile (specifically if Ty is already const)
template <class Ty>
struct print_accept_const :
std::enable_if<std::is_same<Ty, Ty>::value || std::is_same<Ty, const Ty>>
{};
template <class Ty, class = print_accept_const<Ty>>
class print
{
print(Ty line)
{
std::cout << line << std::endl;
}
};
Just for reference, I'm using partial template specialization because I'm specializing this print class for std::vector objects, std::set objects, std::unordered_set objects, etc. If there is a way to do this without SFINAE, then I'd be totally open to that.
Edit 1
As asked in a comment, my exact error happens when I try to specialize for std::unordered_set objects.
template <class ValTy>
class print <std::unordered_set<ValTy>>
{
public:
print(std::unordered_set<int> lines) // 'int' instead of 'ValTy' to activate IntelliSense for errors
{
const auto last = --lines.end();
for (auto& line : lines)
{
// IntelliSense, for the line below when 'ValTy' is
// replaced with 'int', says:
//
// no instance of constructor "print<std::unordered_set<ValTy,
// std::hash<ValTy>, std::equal_to<ValTy>, std::allocator<ValTy>>>
// ::print(std::unordered_set<int, std::hash<int>, ...>)" matches
// the argument list. Argument types are (int).
//
print<ValTy> p(line);
}
}
}
Isn't clear what do you exactly want (enable when T is const or isn't const; if you have a function that print value of some types it's necessary wrap it in a template class? Can't you apply SFINAE directly over the function?) anyway... some useful elements...
(1) starting from C++11 there is std::is_const that you can use to check if a type is constant or not
(2) to make an example, a possible way to enable a specialization only for constant types is the following
template <typename T, typename = void>
struct print;
template <typename T>
struct print<T, typename std::enable_if<std::is_const<T>::value>::type>
{
print (T line)
{ std::cout << line << std::endl; }
};
The specialization can be simplified, starting from C++14, using std::enable_if_t
struct print<T, std::enable_if_t<std::is_const<T>::value>>
and, starting from C++17, also using std::is_const_v
struct print<T, std::enable_if_t<std::is_const_v<T>>>
(3) you can enable/disable directly the constructor, but only making is a template one (or SFINAE can't work)
template <typename T>
struct print
{
template <typename U = T,
typename std::enable_if<std::is_const<U>::value, int>::type = 0>
print (U line)
{ std::cout << line << std::endl; }
// possible alternative do-nothing constructor for not const values
template <typename U = T,
typename std::enable_if<! std::is_const<U>::value, int>::type = 0>
print (U)
{ }
};
Observe that, in this case, the SFINAE tests (std::is_same and std::is_const<U>) are (also) over the template parameter of the constructor, U, not over the template parameter of the class, T. Otherwise SFINAE doesn't works.
The first std::enable_if impose that T and U are the same.

template template that accepts a mixture of type and template arguments [duplicate]

Is there any way of detecting whether a class is a normal type or is an instantiation of a template type (meta type) which may include non-type parameters? I came up with this solution:
#include <iostream>
template <template<class...> class>
constexpr bool is_template()
{
return true;
}
template <class>
constexpr bool is_template()
{
return false;
}
struct Foo{};
template<class> struct TemplateFoo{};
template<class, int> struct MixedFoo{};
int main()
{
std::cout << std::boolalpha;
std::cout << is_template<Foo>() << std::endl;
std::cout << is_template<TemplateFoo>() << std::endl;
// std::cout << is_template<MixedFoo>() << std::endl; // fails here
}
however it will fail for templates that mix non-types and types, like
template<class, int> struct MixedFoo{};
I am not able to come up with any solution, except the one in which I must explicitly specify the types in the overloads. Of course this is un-reasonable due to combinatorial explosion.
Related question (not a dupe): Is it possible to check for existence of member templates just by an identifier?
No, there is not.
Note that template classes are not classes themselves. They are templates for classes.
I guess it is not possible.
Anyway, you can use the other way around and let N be deduced:
template<class, class> struct MixedFoo;
template<class C, int N> struct MixedFoo<C, std::integral_constant<int, N>>{};
Now, this returns true as expected:
std::cout << is_template<MixedFoo>() << std::endl; // fails here
Of course, you won't be able anymore to use MixedFoo as MixedFoo<int, 2>, so I'm not sure it's worth it.

SFINAE template specialization not working

#include <iostream>
#include <utility>
using namespace std;
template <size_t N, typename V = int> // works if typename V = void
struct T
{
const static int size = 0;
};
template <size_t N>
struct T<N,typename std::enable_if<(N>10)>::type>
{
const static int size = 1;
};
int main (){
cout << T<9>::size << endl; // 0
cout << T<19>::size << endl;// 0 WHY?
cout << T<10>::size << endl; //0
}
Not sure why the output is the way it is, why isn't the specialization getting picked up?
Right now, the second argument is always int, since it's the default value:
int main (){
cout << T<9, int>::size << endl;
cout << T<19, int>::size << endl;
cout << T<10, int>::size << endl;
}
But the expression typename std::enable_if<(N>10)>::type won't ever yield an int, so your specialization won't be picked. The default type for std::enable_if::type is void.
It will work only if you're sending void in the second argument (of course you don't want that):
int main (){
cout << T<9, void>::size << endl;
cout << T<19, void>::size << endl;
cout << T<10, void>::size << endl;
}
To make it work like you want, you must either make the default argument to be void, or make your constraint to always yield an int type.
template <size_t N, typename V = void>
struct T
{
const static int size = 0;
};
Or alternatively make your constrain yeild the type of your default argument:
typename std::enable_if<(N>10), int>::type
In fact, you could put any type and it wouldn't change a thing. As long as the two type matches:
// silly but works, the two types are the same.
template<size_t N, typename = decltype(std::cout)>
struct T { /* ... */ };
template <size_t N>
struct T<N, typename std::enable_if<(N>10), decltype(std::cout)>::type> { /* ... */ };
Due to the template parameter with default argument in your primary template, when you write T<19>::size, what you're really writing is T<19, int>::size.
Now, there exist partial specializations for your template, thus, when figuring out which specialization a simple-template-id like T<19> refers to, the compiler will try to find a matching partial specialization. To do so, it will look for a match between the actual template arguments 19, int and the template arguments given in your partial specialization
template <size_t N>
struct T<N, typename std::enable_if<(N>10)>::type>
{ // ^-------------- this here -------------^
…
};
The first step is to figure out the arguments for the parameters of your partial specialization itself. In your case, the partial specialization has one parameter template <size_t N>. This parameter is deduced by comparing the parameterized type T<N, typename std::enable_if<(N>10)>::type> to the actual type T<19, int>. Deduction yields 19 as argument for parameter N. Substituting back into the specialization, we get
struct T<N, void>
as the deduced argument list for our specialization because std::enable_if<true>::type is void. The argument list 19, void is not the same as 19, int, therefore, the specialization is not a match and the primary template will be used.
So to make this work, you have to make sure that the type produced by your std::enable_if argument matches the default argument of the last parameter of the primary template, i.e., either change the default argument to be void
template <size_t N, typename = void>
struct T
{
…
};
or make your std::enable_if produce int in the cases where you want the specialization to be used
template <size_t N>
struct T<N, std::enable_if_t<(N>10), int>>
{
…
};

Why is template specialization of variadic templates different from specialization of non-variadic templates?

I'm don't understand why template specialization is different for variadic templates than for regular (i.e., non-variadic) templates. For example I have a template and a specialization like:
enum class MF : int {
ZERO = 0,
ONE = 1,
TWO = 2
};
// --------- Specialization -------------
template <MF mf>
class Stat{
public:
Stat(std::string msg) {
cout << "Generic Stat construtor: " << msg << endl;
}
};
// --------- Template Specialization -------------
template<>
class Stat<MF::ONE>{
public:
Stat(std::string msg) {
cout << "Specialized Stat constructor: " << msg << endl;
}
};
I have specialized with a specific value of the MF enumeration.
Now if I want to specialize a variadic template I can't specialize the variadic template parameters with a specific value of the MF enumeration (e.g., MF::ONE), I can only specialize with a type, (e.g. MF).
// --------- Variadic Template -------------
template<MF mf, typename... E>
class Var{
public:
Var(std::string msg){
cout << "Generic Var constructor: " << msg << endl;
}
};
// --------- Variadic Template Specialization -------------
template<>
class Var<MF::TWO, MF>{
public:
Var(std::string msg){
cout << "Specialized Var constructor: " << msg << endl;
}
};
I would like to specialize my variadic template for a specific MF value, but it doesn't appear that I can.
Is there some aspect of the language that I'm missing that would allow me to do what I want? Something along the lines of:
template<>
class Var<MF::TWO, MF::ONE>{
public:
Var(std::string msg){
cout << "Specialized Var constructor: " << msg << endl;
}
};
Complete example can be found here
If I understood you correctly, you want your variadic template to be parameterized with any number of MF values. Well, don't declare it like this :
template<MF mf, typename... E>
... which declares any number of type parameters, but like this :
template<MF mf, MF... moreMF>
... which declares any number of non-type parameters of type MF.
That is to say, explicit specialisation is no different for variadic templates, but you somehow forgot how to parameter in-between.
templates are not macros. typename... does not mean "take any number of comma delimited strings". typename... means "take 0 or more types".
MF::ONE is not a type.
Attempting to pass MF::ONE where the template expects typename... is an error, because the template is asking for a typename not a value.
You can write a template class that takes 0 or more MFs. You can write one that takes zero or more types. You cannot write a template class that takes 0 or more MFs or types. C++ does not support that.
If you must pass values via type, there is std::integral_constant< TYPE, VALUE >, which is a type that wraps an integral constant value.
template<MF mf, typename... E>
class Var;
template<>
class Var<MF::TWO, std::integral_constant<MF, MF::ONE>>{
public:
Var(std::string msg){
cout << "Specialized Var constructor: " << msg << endl;
}
};
you'd instantiate this by Var<MF::TWO, std::integral_constant<MF, MF::ONE>>.
You can create aliases:
template<MF mf> using MF_t=std::integral_constant<MF, mf>;
to make your code look nicer:
template<MF mf, typename... E>
class Var;
template<>
class Var<MF::TWO, MF_t<MF::ONE>>{
public:
Var(std::string msg){
cout << "Specialized Var constructor: " << msg << endl;
}
};
int main() {
Var<MF::TWO, MF_t<MF::ONE>> instance("hello");;
}
For metaprogramming purposes, sometimes it is a wise idea to write your templates as always taking types, and never taking constants, and when you need a constant stuff it into a type like above. Then template<class...>class X will match any such template.
You can gain access to the value of a std::integral_constant with ::value or by constructing it and converting it to a value of that type. (In C++1y and many C++11 compilers, that conversion is constexpr).
So
MF x = MF_t<MF::ONE>{};
MF y = MF_t<MF::ONE>::value;
will set x and y to MF::ONE. In both cases, the right hand side should be a compile time expression, so:
MF_t< MF_t<MF::ONE>{} >
is a valid type (if a silly written one) that is the same as MF_t< MF::ONE >.

How to disambiguate function templates that differ only by return type?

I have noticed that there is an asymmetry between the signature used to distinguish unique template functions, and the signature used to distinguish unique functions (including those instantiated from template functions).
In particular, template functions that differ only by return type are considered to be unique, whereas functions that differ only by return type are considered to be redundant.
Therefore, I have a corresponding question about how to disambiguate between function templates that differ only by return type, at the point of instantiation:
#include <iostream>
template<typename T>
long foo(T)
{
std::cout << "long" << std::endl;
return 0;
}
template<typename T>
char foo(T)
{
std::cout << "char" << std::endl;
return '\0';
}
int main()
{
double d = 0.0;
long n = foo(d); // <- Ambiguous: How to specify the template function to use?
}
In the above code, the instantiation of the template function foo is ambiguous precisely because of the asymmetry I've just mentioned. The presence of the two template function definitions is legal, but the instantiation is illegal, even though the return type is specified in the same line of code.
I am asking this question purely for theoretical learning purposes. Perhaps this code construct, in real life, would be a sign of poor design. Perhaps it would never arise in real life. Also, I can envision different ways of overcoming this issue by changing the template definitions (or by making other changes).
However, I would nonetheless like to know if, keeping the template definitions unchanged, it is possible to disambiguate between these two template functions at the point of instantiation.
When using templates you can actually disambiguate the two different overloads. It ain't pretty but works:
long n = static_cast<long(*)(double)>(&foo)(d);
If you really need to have two function templates having the same names, same list of parameters, but different return types, you have no choice but differentiate the two by making the return type a template parameter:
template <typename R, typename T>
R foo(T);
IIRC there is partial function template specialization in C++11, although I could not find anything about it in the standard. If there is, this should work:
//partial function template specializations: C++11 only!
template <typename T>
long foo<long, T>(T)
{
std::cout << "long" << std::endl;
return 0;
}
template<typename T>
char foo<char, T>(T)
{
std::cout << "char" << std::endl;
return '\0';
}
Or else, in C++03:
template <typename R, typename T>
struct FooImpl;
template <typename T>
struct FooImpl<long, T>
{
static long doIt(T)
{
std::cout << "long" << std::endl;
return 0;
}
};
template <typename T>
struct FooImpl<char, T>
{
static char doIt(T)
{
std::cout << "char" << std::endl;
return '\0';
}
};
template <typename R, typename T>
R foo(T t)
{
return FooImpl<R, T>::doIt(t);
}
In both cases, your main would look like this:
int main()
{
double d = 0.0;
long n = foo<long>(d); // specify the return type only
auto c = foo<char>(n);
}