Default parameters of struct templates - c++

I have a template struct tree_parse_info declared as follows:
template <
typename IteratorT,
typename NodeFactoryT,
typename T
>
struct tree_parse_info
{
// ...
};
The compiler allows the follows code:
tree_parse_info<> m_info;
Why does this code compile even though we do not have default template parameters for the template struct tree_parse_info ?

If the class has been forward declared previously, you won't need to restate the default parameters. For example:
// forward declare only
template <typename T = int, size_t N = 10>
struct array;
// actual definition
template <typename T , size_t N>
struct array
{};
int main(void)
{
array<> a; // uses the defaults it saw in the forward-declaration
}
Look above your actual definition to see if you forward declared it.
By the way, if you give defaults at one place, and differing defaults at another, you'll get a compile error:
template <typename T = int, size_t N = 10>
struct array;
// error: redefinition of default parameter
template <typename T = double , size_t N = 2>
struct array
{};
Try giving the code you showed us defaults that can't possibly accidentally match, like:
struct stupid_special_tag_type_lolroflwat {};
template <
typename IteratorT = stupid_special_tag_type_lolroflwat,
typename NodeFactoryT = stupid_special_tag_type_lolroflwat,
typename T = stupid_special_tag_type_lolroflwat
>
struct tree_parse_info
{
// ...
};
If you get redefinition errors, you know you've given it defaults in some other location.

When I compile your code with g++, I get the following error:
tp.cpp:10: error: wrong number of
template arguments (0, should be 3)
So you must be compiling something other than the code you are showing us.

Related

How to add member function of template class only on specific types using type_traits | C++

template <typename T>
class A
{
template <std::enable_if_t<std::is_signed_v<T>, bool> = true>
constexpr value_type determinant()
{
}
}
I wanna determinant function is instantiated only when type T is signed type
so I try this codes, but when type T is unsigned compiler still try instantiate determinant function.
I don't wanna specialize template function about each signed type T.
I want to use std::is_signed_v type traits to simplify codes
With SFINAE you can enable/disable a method of a class when the condition tested is related to a template parameter of the method itself. Doesn't works testing a template parameter of the class.
You can solve the problem passing through a method template parameter that is defaulted with the type of the class/struct template parameter.
For example
#include <iostream>
#include <type_traits>
template <typename T>
struct foo
{
template <typename U = T,
std::enable_if_t<std::is_signed_v<U>, bool> = true>
constexpr std::size_t bar ()
{ return sizeof(T); }
};
int main()
{
foo<signed int> fs;
foo<unsigned int> fu;
fs.bar(); // compile
//fu.bar(); // compilation error
}
The problem of this solution is that you can "hijack" it explicating a template parameter
fu.bar(); // compilation error
fu.bar<int>(); // compile
To avoid the hijacking risk, you can (other solutions are possible but this is my preferred) add a non-type variadic template parameter before U
// .......VVVVVVVV add this to avoid the hijacking problem
template <int ...,
typename U = T,
std::enable_if_t<std::is_signed_v<U>, bool> = true>
constexpr std::size_t bar ()
{ return sizeof(T); }
// ...
fu.bar(); // compilation error
fu.bar<int>(); // compilation error
fu.bar<1, 2, 3, int>(); // compilation error

Using a specialization of variadic template as a template argument

Consider the following:
template <class...>
struct MyT;
template <class T>
struct MyT<T> {};
template <template <class> class TT = MyT> struct A {}; // fine
using B = A<MyT>; // does not compile
int main() {
return 0;
}
When MyT is used as a default argument of A, the compiler (g++ 5.4.0) is happy. However, when it is used to instantiate A, the story is different:
temp.cpp:19:16: error: type/value mismatch at argument 1 in template parameter list for ‘template<template<class> class TT> struct A’
using B = A<MyT>;
^
temp.cpp:19:16: note: expected a template of type ‘template<class> class TT’, got ‘template<class ...> struct MyT’
I can fix it by introducing an alias:
template <class T>
using MyTT = MyT<T>;
using B = A<MyTT>; // fine
The question: what is the reason for the error and is there a solution without introducing an alias?
EDIT Please note that A is declared to have a template template parameter as shown and that is not given for change.
You cannot do that and you cannot use such a type as a default parameter. The fact that it seems to be accepted as long as you don't rely on it doesn't mean that the default parameter is a valid one.
Consider the following code that explicitly uses the default type:
template <class...>
struct MyT;
template <class T>
struct MyT<T> {};
template <template <class> class TT = MyT> struct A {}; // fine
int main() {
A<> a;
return 0;
}
The error is quite clear:
template template argument has different template parameters than its corresponding template template parameter
Partial specializations are not taken in account in this case, thus the two declarations differ.
You should either declare A as:
template <template <class...> class TT = MyT> struct A;
Or declare somewhere a type that is constrained to a single argument, as an example by means of an using declaration as you did.
First, the default argument doesn't work either.
Second, template template arguements are a strange beast. It would make sense if a template template argument would take anything that could be instantiated with the signature described in the template template argument.
That is not how it works.
Instead it works the other way around.
template<template<class...>class Z> struct foo {};
template<template<class >class Z> struct bar {};
template<class...>struct a{};
template<class >struct b{};
foo will accept a or b.
bar will accept only b.
The correct response to this, once you understand it, is "what the hell?". If you aren't responding "what the hell" back up and see if you can understand it. This basically works backwards from typical typing for arguements in C++; it behaves more like a return type than an argument. (Learn the terms contravariance and covariance if you want to see some of the language that lets you talk about this directly)
This is quite non-intuitive, and why it works this way exactly would involve tracking down the pre-history of C++.
But, as a benefit, a template<class...>class argument is in effect an "any template that only takes type parameters". I find this highly useful.
As a downside, template<class>class arguements are almost completely useless.
Tl;dr: make your template<template parameters be template<template<class...>class, and metaprogram only with templates that only take types. If you have a template that takes values, write a type wrapper that replaces a requirement for a std::size_t X with a std::integral_constant< std::size_t, X >.
Forgetting for a moment the question of "Why would you do this?",
the first version would work if you hadn't done any template specialization.
template <class T>
struct MyT { };
template <template <class> class TT = MyT> struct A
{};
using B = A<MyT>;
With template specialization, the compiler must determine the best match, but since you haven't ever actually provided any template arguments it's ambiguous.
When you introduce MyTT you are using a single template argument, and the compiler is smart enough to see that you have a specialization when there is only one arg:
template <class T>
using MyTT = MyT<T>;
It chooses the specialization instead of the variadic version in this case.
But now we circle back to the grand question... why? Unless within A you're always instantiating MyT with a specific class, it's pointless to use A at all:
template<template<class> class TT = MyT> struct A
{
// use an instance of TT??
TT<int> myInstance; // forced to choose `int`
};
I would like to split your question into 2 parts.
A) Consider the template of your structure is simpler
template <class T>
struct TestStruct {
};
template <
template <class>
class TT = TestStruct
>
struct A
{
int a; // sorry to modify this. This help to show how it works
};
int main() {
A< TestStruct > b;
b.a; // it works!
return 0;
}
It works because of the template class TT only accept the template with < class... > template. The specializated class is not count on this ( because the underlying of it is still template < class ... > )
B) even you update your struct A to the template< class... > one, you still have one more problem. What is the template argument of TT? Please see the example below
template <class...>
struct MyT;
template <class T>
struct MyT<T> {
int a;
};
template <
template <class...>
class TT = MyT
// may be you need to add some more typename here, such as
// typename T1, ... , and then TT<T1> a;
>
struct A
{
TT<int> a;
// Here ! TT is a template only, do not have the template parameters!!!
};
int main() {
A< MyT > b;
b.a; // it works!!
return 0;
}
But, if you really cannot update the signature of those definitions, you can do a proxy class
template< class T >
struct Proxy : MyT<T>
{
};

Using non-type template argument during partial specialization

Consider the following structs:
//Implementations provided elsewhere
struct A { A(int i, double d, std::string s); /* ... */ };
struct B { B(double d1, double d2); /* ... */ };
I have two conversion classes whose template signatures look like:
TupleAs< A, int, double, std::string > via1 { ... };
ArrayAs< B, double, 2 > via2 { ... };
Predictably, TupleAs converts a triplet of int,double, and std::string values into an object of type A. Similarly, ArrayAs converts a pair of two double values into an object of type B. (And yes, there are reasons why I cannot call the A and B constructors directly.)
Improving the syntax
I would like to change the syntax so I can do the following:
TupleAs< A(int,double,std::string) > via1 { ... };
ArrayAs< B(double,2) > via2 { ... };
which, I think, is more descriptive of a conversion process. The TupleAs template declaration and corresponding partial specialization would look like this:
template <typename T> struct TupleAs;
template <typename T, typename ... Args>
struct TupleAs<T(Args...)> { ... };
Compiler errors
However, if I try to do something similar with the ArrayAs version:
template <typename T> struct ArrayAs;
template <typename T, typename U, unsigned N>
struct ArrayAs<T(U,N)> { ... };
I get the following errors in clang (3.6) when trying to instantiate it (ArrayAs< B(double,2)> test;):
typeAs.cpp:14:22: error: unknown type name 'N'
struct ArrayAs<T(U,N)>{
^
typeAs.cpp:14:10: warning: class template partial specialization contains a template parameter that cannot be deduced; this partial specialization will never be used
struct ArrayAs<T(U,N)>{
^~~~~~~~~~~~~~~
typeAs.cpp:13:45: note: non-deducible template parameter 'N'
template<typename T, typename U, unsigned N>
^
The gcc error diagnostic is a little different, but I won't post it here.
I admit that my templating skills should be better than they are, and I also concede that an analogous std::function<B(double,2)> declaration clearly is nonsense. But can someone tell me why the particular syntax I'm trying to achieve is not allowed? I looked through the C++14 standard and had trouble finding the relevant portion, and I'm having trouble interpreting the clang diagnostic message.
When you specialize TupleAs:
template <typename T, typename ... Args>
struct TupleAs<T(Args...)>
You are basically overloading the notation for a function. You are specializing on a function that takes Args... and returns a T. That is a type. You may not be using that function as a function, or really ever think about it as being a type, but that is what it is.
On the other hand, here:
template <typename T, typename U, unsigned N>
struct ArrayAs<T(U,N)> { ... };
There is no such thing as a function that takes N. It could take unsigned, but it can't take a value. There is just no such reasonable thing. From your example, B(double, 2) simply does not make sense. At best, you could write something that would allow:
template <unsigned N> using size_ = std::integral_constant<size_t, N>;
ArrayAs< B(double,size_<2>) >
Or even:
ArrayAs< B(std::array<double, 2>) >
since now we're back to using types everywhere. Whether you prefer that or not is personal preference.
The key here is that types are first-class citizens when it comes to all things template metaprogramming, and values should be avoided where possible.
template <typename T> struct ArrayAs;
template <typename T, typename U, std::size_t N>
struct ArrayAs<T(std::array<U,N>)> { ... };
works, as would:
template<class T>
struct to_array;
template<class T, size_t N>
struct to_array< T[N] > { using type = std::array<T, N>; };
template<class T>
using arr = typename to_array<T>::type;
then:
ArrayAs< Bob( arr<int[3]> ) > some_var;
live example.
Sadly, directly using ArrayAs< Bob( int[3] ) > doesn't work due to how arrays in function types decay to pointers.

enable_if cannot be used to disable this declaration

I evidently have not enough experience with SFINAE to handle this problem. I actually have the impression that it worked until now, and this kind of problem started to appear like in the last half an hour, everywhere in my code.
#include <iostream>
using namespace std;
template <unsigned int N, typename = typename enable_if <N >= 100> :: type>
struct more_than_99
{
};
int main()
{
more_than_99 <0> c;
}
It says
No type named 'type' in 'std::__1::enable_if<false, void>'; 'enable_if' cannot be used to disable this declaration
on the line corresponding to the template declaration. What is going on? I have always used this kind of syntax to enable and disable my template classes and it has always thrown errors on the line of instantiation, rather than on the line of the declaration..
Could you please pedantically explain what am I doing wrong here?
The other answers are correct about why the error happens at the template definition rather than the instantiation.
I need an error to be thrown when trying to instantiate something like `more_than_99 <0> x;' on the line where I try to instantiate it. Something like "hey, this type doesn't exist".
How about something like this?
template <unsigned int N, bool B = (N>=100)>
struct more_than_99;
template <unsigned int N>
struct more_than_99<N,true>
{};
int main()
{
more_than_99 <0> c; // error: implicit instantiation of undefined template 'more_than_99<0, false>'
}
To make it a bit more robust, and to attempt to prevent accidentally instantiating more_than_99<0,true>, this also works (C++11):
template <unsigned int N, bool B>
struct _impl_more_than_99;
template <unsigned int N>
struct _impl_more_than_99<N,true>
{};
template <unsigned int N>
using more_than_99 = _impl_more_than_99<N, (N>=100)>;
int main()
{
more_than_99 <0> c; // error: implicit instantiation of undefined template '_impl_more_than_99<0, false>'
}
Although the error message references the _impl_ type.
You could hide the _impl_ in a detail namespace or something, and just document the more_than_99 alias as if it were the actual type.
However, you will not be able to prevent malicious instantiation of _impl_more_than_99<0,true>.
N is not a dependent non-type template parameter; [temp.dep.temp]/p2
A non-type template-argument is dependent if its type is dependent or the constant expression it specifies is value-dependent.
Therefore instead of a substitution failure occurring, the error is emitted directly from the ill-formed code.
enable_if makes sense if you have a class specialization (or a function overload). It is used to choose between one implementation and another depending on a template parameter, not to trigger an error if the condition is not met.
The idea is "enable this specialization if the condition is met, otherwise fall back to the non-specialized version".
In your case, you probably want something like this:
#include <iostream>
#include <type_traits>
using namespace std;
template<unsigned int N, typename = void >
struct more_than_99
{
// Implementation if N <= 99
enum { value = false };
};
template <unsigned int N>
struct more_than_99<N, typename enable_if <N >= 100> :: type>
{
// Implementation if N >= 100
enum { value = true };
};
int main()
{
cout << more_than_99 <0>::value << endl; //false
cout << more_than_99 <100>::value << endl; //true
}
From http://en.cppreference.com/w/cpp/types/enable_if: (emphasis mine)
This metafunction is a convenient way to leverage SFINAE to conditionally remove functions from overload resolution based on type traits and to provide separate function overloads and specializations for different type traits. std::enable_if can be used as an additional function argument (not applicable to operator overloads), as a return type (not applicable to constructors and destructors), or as a class template or function template parameter.
You cannot use it to enable or disable a class or a struct.
Perhaps you are looking for something like:
namespace detail
{
struct more_than_99 {};
template <bool> Helper;
template <> Helper<true>
{
using type = more_than_99;
};
}
template <unsigned int N> struct selector
{
using type = typename detail::Helper<N >= 100>::type
};
using type = selector<10>::type; // Error.
using type = selector<100>::type; // OK.
// type == detail::more_than_99
Use static assert:
template<unsigned int N>
struct more_than_99
{
static_assert(N >= 100, "N must be more than 99");
};
more_than_99<1> m1;
Results in compilation error something along the lines of:
testM99.cpp:6:3: error: static_assert failed "N must be more than 99"
static_assert(N >= 100, "N must be more than 99");
^ ~~~~~~~~
testM99.cpp:12:19: note: in instantiation of template class 'more_than_99<1>'
requested here
more_than_99<1> m1;

What does `class =` inside template mean?

I found this construct in C-code:
template<typename T, class = decltype(std::declval<T>() < std::declval<T>())>
struct check : std::true_type {};
Now I understand what it does but I don't understand how it works. It throws a compile error if type T doesn't support the <-operator. But, apparently, when changing class to something else, the whole thing won't compile and throws a Syntax Error.
What does class = sometypename mean?
class is the same as typename here. You could also do this:
template<typename T, typename = decltype(std::declval<T>() < std::declval<T>())>
struct check : std::true_type {};
You can specify default values for template arguments. For example
template<typename X = int> struct test { };
You can also leave off the name of the template arguments if you don't use them:
template<typename = int> struct test { };
So in your example, the second template parameter is just an unnamed parameter with a default argument.
The concept that makes this work is known as SFINAE (substitution failure is not an error) and is used to implement std::enable_if<> etc. http://en.cppreference.com/w/cpp/language/sfinae