Template type deduction in container initialisation - c++

I understand that a template type parameter can be defaulted, however, this doesn't work when I attempt to construct a std::vector without supplying the template argument:
#include<vector>
template <typename Int = int>
struct integer{
Int i;
};
int main(){
integer num;
std::vector<integer> vec;
return 0;
}
This code returns a type value mismatch (compiler explorer link). Is it possible to fix this without writing std::vector<integer<int>>

If you have a function with a default parameter:
int foo(int bar=0);
To call this function you still have to write:
int n=foo();
attempting to write int n=foo; will not work, of course. For the same reason if your template's parameters are defaulted you still have to use
integer<>
to instantiate the template instead of
integer

You could create an alias integer_t for interger<> using using.
template <typename Int = int>
struct integer{
Int i;
};
using integer_t = integer<>;
int main(){
std::vector<integer_t> vec; // No error.
}
The reason why std::vector<integer> did not work is explained in this answer.

You can provide a wrapper that accepts template template parameter (read below):
template <template <typename> class TT,
typename T = int>
using my_vector = std::vector<TT<T>>;
my_vector<integer> vec{}; // works
my_vector<integer, long> vecLong{}; // using non-default type
Templates have different types of template parameters:
Each parameter in a parameter-list may be:
a non-type template parameter;
a type template parameter;
a template template parameter.
template <typename>
struct with_type_parameter{};
template <template <typename> class>
struct with_template_template_parameter{};
So, there is an explicit difference for compiler between your integer<> and integer.
The first one is a type explicitly instantiated from a template parameter.
The second one is a template template.
So, when you instantiate a class template std::vector with a template template integer instead of a type integer<> for an argument, you get the error you have. Because these two things are different for class templates.
However, there is a different behavior fo function templates, since they are allowed for overloading.
Here is an example for you: https://godbolt.org/z/cbnss17aj
template <typename>
struct with_typename{};
// can't have that. class template can only be specialized when declared
// template <template <typename> class>
// struct with_typename{};
#include <iostream>
template <typename>
void with_typename_fn()
{
std::cout << "type" << std::endl;
}
// perfectly fine, since function template overloading is allowed in C++
template <template <typename> class>
void with_typename_fn()
{
std::cout << "template template" << std::endl;
}
template <typename>
struct non_defaulted{};
template <typename = int>
struct defaulted{};
int main()
{
with_typename_fn<int>(); // ok
with_typename_fn<non_defaulted>(); // also ok
with_typename_fn<defaulted>(); // also ok, but will resolve to template template specialization!
}
Output:
type
template template
template template

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

Generic `Is_enabled` SFINAE structure

I aim to implement a structure template that can be used to detect either if a template substitution is well formed or will fail. An example of usage is to provide two versions of template functions depending on whether the template parameter is comparable or not.
It can be solved quite easily if one provides structures for each scenario explicitly, e.g. whether there exists an equality operator for the template type, as shown here. But I failed to implement a structure that would accept (almost) arbitrary construct as a template argument.
The "best" approach I have reached so far uses template template argument. It compiles, but it does not fit the case when the argument substitution should be well formed.
#include <iostream>
#include <type_traits>
template <typename T = void, typename...>
using Enable = T;
template <bool Cond, typename T = void>
using Enable_if = typename std::enable_if<Cond, T>::type;
template <typename T, template<typename> class X, typename = void>
struct Is_enabled : std::false_type {};
template <typename T, template<typename> class X>
struct Is_enabled<T, X, Enable<X<T>>> : std::true_type {};
/// An example of construct
template <typename T>
using Equals = decltype(std::declval<T>() == std::declval<T>());
template <typename T>
using Enabled_eq = Enable_if<Is_enabled<T, Equals>::value>;
template <typename T>
using Disabled_eq = Enable_if<!Is_enabled<T, Equals>::value>;
template <typename T>
Enabled_eq<T> foo()
{
std::cerr << "enabled!" << std::endl;
}
template <typename T>
Disabled_eq<T> foo()
{
std::cerr << "disabled!" << std::endl;
}
struct A {};
int main(int /*argc*/, const char* /*argv*/[])
{
foo<int>(); /// should print "enabled!"
foo<A>(); /// should print "disabled!"
return 0;
}
In case of int, it should obviously print "enabled!", and in case of A it should print "disabled!". But it always prints "disabled!", so the specialization of Is_enabled is never done.
Am I somewhat close to a correct solution, or will it be more complicated?
The third template parameter of Is_enabled defaults to void. This is what the compiler will use in the Is_enabled<T, Equals> instantiation. That is, Is_enabled<T, X, Enable<X<T>>> : std::true_type {}; can be used only if Enable<X<T>> evaluates to void. By explicitly passing a template argument X<T> to class template Enable declared as:
template <typename T = void, typename...>
using Enable = T;
you actually create an alias for X<T> itself, and the void type (the default one, needed for the dispatching to work) is not used at all. In your case, X<T> is the result of the decltype specifier. For foo<A>() it does lead to instantiation failure. For foo<int>(), however, you get the result type of integers comparison which is bool. That is, although there is no subsitution failure, the compiler cannot use the class template specialization, because it is specialized for void, not bool.
In order to fix the code, you should rewrite Enable to always result with void:
template <typename...>
using Enable = void;
This is also known as std::void_t.

Syntax for an instance of a class template as a non-type template parameter

I can't find the right syntax even after reading cppreference on template params. The following doesn't compile, but hopefully describes what I want to do. What's the correct syntax?
template <class DisplayType>
class DisplayAdapter : public DisplayType
{
};
template<template <typename> DisplayAdapter>
class Painter // Takes an instance of DisplayAdapter, not a type!
{
}
Here is how it's supposed to be used:
struct S{};
int main()
{
DisplayAdapter<S> concreteAdapter;
Painter<concreteAdapter> p;
return 0;
}
Here's Ideone snippet for the whole thing: https://ideone.com/dvbYt8
What you're wanting is not a template template parameter.
A template template parameter is used to pass templates around, like this:
template<template<typename> typename Container>
struct foo {
Container<int> container;
};
int main() {
foo<std::vector> f;
}
As you can see, you can pass template names around with that. Remember that templates are not types, but a blueprint for type, and the language (and the standard) is not treating templates the same way as types.
I assume with your examples your trying to use non-type template parameters?
A non-type template parameter is a template parameter that is a value instead of a type. It can be of any integral types, reference type and pointer type.
For example, look at std::array:
std::array<int, 10> tenInts;
Notice the second parameter is a number. This is because std::array look something like this:
template<typename T, std::size_t N>
struct array { /* ... */ };
The second parameter is an unsigned long int.
You can also pass references and pointers as template parameter:
template<int& i> struct foo {};
template<int* i> struct bar {};
int main() {
// Need at least internal linkage in C++14 and older
// No linkage required since C++17, only static needed.
static int num = 0;
foo<num> f;
bar<&num> b;
}
You can even pass a pointer to any type as reference template parameter:
struct stuff {};
template<stuff& s> struct foo;
int main() {
static stuff s{};
foo<s> f; // works!
}
This seem to be closer to what you wanted. However, you seem to have many many different type you want to send as template parameter, as the type of the instances you want to pass around are templated. For that you'll need C++17 template auto feature:
template<auto& da>
struct Painter {
// ...
};
int main() {
static DisplayAdapter<S> concreteAdapter;
Painter<concreteAdapter> p;
}
And done!
If you don't have C++17 with you don't worry, and simply pass the display type along with your instance (C++14 example):
template<typename DT, DisplayAdapter<DT>& da>
struct Painter {};
// Internal linkage
DisplayAdapter<S> da;
int main() {
Painter<S, da> painter;
}
If it was some simple type like int you would just use type instead of typename keyword e.g
template <int x>
or
template <std::vector<int>::value_type x>
But you can't put object of arbitrary type here.
More about non-type template parameters can be found in another Q&A
You need to add class before DisplayAdater
template <class DisplayType>
class DisplayAdapter : public DisplayType
{
};
template<template <typename> class DisplayAdapter>
class Painter // Takes an instance of DisplayAdapter, not a type!
{
};
https://godbolt.org/g/mV7YRC

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>
{
};

Avoid angle brackets in default template

If I have a template class with a default template type, I have to write the template angle brackets. Is it somehow possible to avoid this?
Example:
template <typename T=int>
class tt {
public:
T get() { return 5; }
};
...
tt<> t; // how to avoid <>
std::cout << t.get() << std::endl;
Until now i've did this by a separate namespace and redeclaring the class:
namespace detail_ {
template <typename T=int>
class tt {
public:
T get() { return 5; }
};
}
class tt : public detail_::tt {}
...
tt t;
std::cout << t.get() << std::endl;
The problem is, if I want to use the class with an other type I have to go over namespace detail_. Is there another solution, which I didn't see yet.
... if I want to use the class ...
This is a common source of confusion. A class template is not a class, but a template from which classes are generated. The angle brackets is what tells the compiler that you want to generate a class out of the class template with the given template arguments, without the angle brackets what you have is a template.
template <typename T = int>
struct TemplateClass { /*...*/ };
template <template <typename> class T>
void f() {
T<int> t; // ...
}
template <typename T>
void g() {
T t; // ...
}
f<TemplateClass>(); // Accepts a template with a single type argument
g<TemplateClass<> >(); // Accepts a type, that can be generated out of the template
The language does not allow the coexistence of a template and a type with the same name in the same namespace, so the answer is that it cannot be done. You can create a type alias but you will have to give it a different name.
You can use typedef...
typedef tt<> tt_;
And then simply use tt_.
Since C++17, because of class template argument deduction, things have changed.
tt and tt<> are not the same thing: types and class templates were different and continue to be treated differently.
Anyway in simple scenarios like the one in your example, C++17 assumes what you mean and the <> aren't needed anymore.
Further details:
Template default arguments (specifically https://stackoverflow.com/a/50970942/3235496);
Why is <> required when specifying a template class which has defaults for all its template parameters?