Constructor with template arguments - c++

I have a Dynamic class that can store different types : int, double, std::vector<int>, std::vector<double>, etc. I have about 50 of such types.
I would like my Dynamic type to have a constructor where we give two informations:
The type to be stored
Arguments used to construct the type inside the Dynamic class
I am looking forward to something such as
const Dynamic x<std::vector<double>>{10};
to construct in place a Dynamic object that has a std::vector<double> of length 10.
PS: I am allowed to use C++11 and I am not allowed to use RTTI

Constructor template arguments must be deduced. They cannot be provided explicitly. You can get around this by providing a type tag which encodes the wanted template argument and passing it as an additional constructor argument. For example :
#include <utility> // For std::forward
struct foo
{
// Helper tag type
template<class T>
struct type_tag {};
// The template argument T is deduced from type_tag<T>
template<class T, class ... Args>
foo(type_tag<T>, Args&&... p_args)
{
T value{ std::forward<Args>(p_args)... };
}
};
int main()
{
// Provide a type tag so the template argument can be deduced
foo bar{ foo::type_tag<int>{}, 5 };
}

As long as you don't mind putting the type info next to Dynamic rather than the variable name you can do this with variadic args:
#include <iostream>
#include <vector>
template <typename T>
class Dynamic
{
public:
template <typename... Args>
Dynamic(Args... args) : data_(args...)
{
}
T data_;
};
int main()
{
const Dynamic<std::vector<double>> x{10};
std::cout << x.data_.size() << std::endl;
}

Related

Are there any means to allow for more complex type inference in C++ templates?

Suppose I have a template function:
template<class T>
void whenMatchesType(std::function<void(T*)> action) { ... }
I might invoke this like so:
anObject.whenMatchesType<SomeType>([=](SomeType *value) {
// ...
});
Although C++ is capable of inferring template parameters from arguments of simple, non-template types, I don't seem to be able to omit explicitly specifying the type (as <SomeType>) in this case - even though it is provided as a type parameter to the first argument.
Is there some change to my code - or to my compilation - through which I might avoid this redundancy?
If you need acces to the parameter type you can still take in the callable as it's own template parameter, then use type traits to extract the information.
Here is a simple example.
#include <iostream>
#include <functional>
template <typename T>
struct parameter_type;
template <typename ReturnType, typename ParameterType>
struct parameter_type<std::function<ReturnType(ParameterType)>> {
using ParameterT = ParameterType;
};
template <typename T>
using param_t = typename parameter_type<decltype(std::function{std::declval<T>()})>::ParameterT;
template<class T>
void whenMatchesType(T) {
using Parameter = param_t<T>;
static_assert(std::is_same_v<Parameter, int>, "Only callables that take int as parameter is allowed");
}
int main() {
whenMatchesType([](int){});
//whenMatchesType([](double){});
}

Template parameter can't be deduced on implicitly constructed argument

I would like to have the following code in c++17:
#include <iostream>
#include <string>
#include <type_traits>
#include <functional>
class Foo;
template<class T>
class Bar {
public:
std::function<T(Foo&)> m_fn;
template<class Fn>
Bar(Fn fn) : m_fn(fn) {};
T thing(Foo &foo) const {
return m_fn(foo);
}
};
template<class Fn>
Bar(Fn) -> Bar<decltype(std::invoke(std::declval<Fn>(),
std::declval<Foo&>()))>;
class Foo {
public:
Foo() {};
template<class T>
std::vector<T> do_thing(const Bar<T> &b) {
std::vector<T> r;
r.push_back(b.thing(*this));
return r;
}
};
std::string test(Foo &) {
return "hello";
}
int main() {
Foo foo = Foo();
// works
std::vector<std::string> s = foo.do_thing(Bar{test});
// cant deduce T parameter to do_thing
std::vector<std::string> s = foo.do_thing({test});
}
But compiling this gives me "couldn't deduce template parameter ‘T’" on the call to do_thing.
Having do_thing(Bar{test}) fixes this and works fine but equates to some ugly code in the real code equivalent. I would like to have do_thing({test}) or do_thing(test) implicitly construct a Bar and pass that as the argument if possible.
I also don't want to forward declare a variable to pass into do_thing either
Is there some way to guide the inference of template argument T so that the call to do_thing can stay clean?
Edit:
Sorry for the late edit, but the arguments to the Bar constructor are over simplified in the example I included. In reality, there is an extra parameter std::optional<std::string> desc = std::nullopt and that might change in the future (although unlikely). So constructing the Bar inside do_thing would be a bit hard to maintain...
would like to have do_thing({test}) or do_thing(test) implicitly construct a Bar and pass that as the argument if possible.
Unfortunately, when you call do_thing({test}) or do_thing(test), test (or {test}) isn't a Bar<T> object. So the compiler can't deduce the T type and can't construct a Bar<T> object.
A sort of chicken-and-egg problem.
The best I can imagine is to add, in Foo, a do_test() method as follows
template<typename T>
auto do_thing (T const & t)
{ return do_thing(Bar{t}); }
This way you can call (without graphs)
std::vector<std::string> s = foo.do_thing(test);
You get the same result as
std::vector<std::string> s = foo.do_thing(Bar{test});
-- EDIT --
The OP ask
is there any way of preserving the {test} brace syntax? maybe with initializer_list or something?
Yes... with std::initializer_list
template<typename T>
auto do_thing (std::initializer_list<T> const & l)
{ return do_thing(Bar{*(l.begin())}); }
but, this way, you accept also
std::vector<std::string> s = foo.do_thing(Bar{test1, test2, test3});
using only test1
Maybe a little better... another way can be through a C-style array
template <typename T>
auto do_thing (T const (&arr)[1])
{ return do_thing(arr[0]); }
This way you accept only an element.
This happens because {} is not an expression and can only be used in limited ways while doing argument deduction, the parameter must have specific forms in order to succeed.
The allowed parameters types that can be used to deduce template parameters when {} is involved are better expanded in [temp.deduct.call]/1, two of the examples extracted from the cited part of the standard are:
template<class T> void f(std::initializer_list<T>);
f({1,2,3}); // T deduced to int
template<class T, int N> void h(T const(&)[N]);
h({1,2,3}); // T deduced to int
In your example the deduction guide is not used to deduce the T for {test} for the same as above.
foo.do_thing(Bar{test});
is your direct option without using additional functions.

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

Obtain type of member having pointer to member

I am trying to write a template class that will accept any type, as a parameter, which has some public member which can be used as an index (of type int, short, unsigned, etc). I would like my template class to accept the type of object to be used as well as information which field of this type my template class should use as the 'key' field.
I wrote something like below (just an example to give you an idea of how I want to use it), but it doesn't work. The problem is that I am trying to use a pointer to member as the information of which field of the type specified as Value should my template class use to get the Key. I wanted to use decltype to obtain the type of the pointed field, but it doesn't work.
Is there a way to get the type of the field which is pointed by poiter to member? I tried decay function but with no success. Instead of having "int A::*" I want to get just "int".
#include <iostream>
#include <vector>
using namespace std;
template <class V, class M, M member>
struct MyClass
{
using key_type = decltype(member);
vector<key_type> v;
MyClass()
{
v.push_back(1);
// ERROR - error: no matching function for call to 'std::vector<int A::*, std::allocator<int A::*> >::push_back(int)'
}
};
struct A
{
int x;
int key;
};
int main()
{
MyClass<A, decltype(&A::key), &A::key> mc;
}
TL;DR: A few minor corrections to the template parameters:
template <class C, typename Mem, Mem C::*member>
struct MyClass {
using key_type = Mem;
vector<key_type> v;
MyClass()
{
v.push_back(1);
}
};
And the instantiation:
MyClass<A, decltype(A::key), &A::key> mc;
Will do the trick.
Explanation:
You want the third parameter to be a pointer to the member of the class you pass as the first parameter. That requires the pointer to member syntax C::*. The type Mem is the type of the "pointee".
To obtain the type of the member, and not the type of the "pointer to member" you need to supply decltype with either a member access expression, or a qualified id. Hence the change there.
You can reduce the amount of template parameters to 2, by passing only the pointer to member type (as you did originally), and using a meta-function to extract the relevant information from it.
template<typename T>
struct point_to_mem;
// Meta-function to extract type information from a pointer to a member.
template<class C, typename T>
struct point_to_mem<T C::*> {
using member_type = T;
using class_type = C;
};
template <typename PointToMem, PointToMem member>
struct MyClass {
using key_type = typename point_to_mem<PointToMem>::member_type;
vector<key_type> v;
MyClass()
{
v.push_back(1);
}
};
// ...
MyClass<decltype(&A::key), &A::key> mc;
Finally, with C++17, you can make this really economical and reduce the template parameters to 1:
template <auto member>
struct MyClass {
using key_type = typename point_to_mem<decltype(member)>::member_type;
// As before
};
// ...
int main()
{
MyClass<&A::key> mc;
}

c++ parameter pack specification in constructor rather than template

Unlike function declarations with parameter packs, I've found that classes require the type for each argument in the angle brackets...
Component<IntegerPair, int, int> temp(40, 5);
...which seems redundant. Here's how I defined Component:
template<typename T, class... T_Args>
class Component
{
public:
Component(T_Args... args)
: m_data(args...)
{}
T m_data;
};
Is there a way to remove int, int from the above statement?
If so, is it ok to remove it?
Also, is my way of instantiation m_data safe? When using
std::forward<T_Args>(args)... my compiler told me I didn't have a
constructor that could convert all of the argument types.
One way is to make the constructor a template:
#include <utility>
struct IntegerPair {
IntegerPair(int, int) {}
};
template<typename T>
class Component
{
public:
template<typename... T_Args>
Component(T_Args&&... args)
: m_data(std::forward<T_Args>(args)...)
{}
T m_data;
};
int main()
{
Component<IntegerPair> c {1,2};
}
This is functionally equivalent to std::vector and its member function emplace_back. It's perfectly ok, IMO. The error messages are pretty cryptic, as usual in template constructs like this, but this can be mitigated with an appropriate static_assert.
template parameter deduction only work for function calls so the basic pattern to achieve what you want looks like this:
template<typename T, class... T_Args>
Component<T, T_Args...> makeComponent(T_Args&&... args) {
return Component<T, T_Args...>(std::forward<T_Args>(args)...);
}
Usage:
auto c = makeComponent<IntegerPair>(1, 1)