I have a nice and small jack-of-all trades Array type that fits all my needs until now.
template <typename T>
class Array
{
...
public:
int Data; // custom value
virtual void InitData() { Data = 0; }
Array(const Array& array);
template <typename U, typename = std::enable_if<std::is_same<U, T>::value, U>> Array(const Array<U>& array);
template <typename... Ts> Array(const Ts&... items);
void Add(const T& item);
template <typename... Ts> void Add(const T& item, const Ts&... rest);
void Add(const Array& array);
}
The template <typename... Ts> Array(const Ts&... items); lets me do Array<T> array = { ... }, and to assign and return {...} initializer lists. Because none of the constructors are explicit, it is incredibly convenient, but is also the reason I'm stuck now.
I would like to be able to add anything "reasonable" to the arrays. My main use case right now is:
using Curve = Array<float2>;
class Poly : public Array<float2> { using Array::Array; void InitData() override { Data = 1; } };
class Curve2 : public Array<float2> { using Array::Array; void InitData() override { Data = 2; } };
class Curve3 : public Array<float2> { using Array::Array; void InitData() override { Data = 3; } };
The std::is_same<> stuff above is specifically to be able to treat all the curves as the same but not the same: the curve types of different degree, and everything is nicely "statically typed", so all I do in a function like DrawCurve(const Curve&) is check the degree and then take appropriate action. Curve is a nice alias for Array, and Curve2 etc. are degree specializations. It works very nicely.
When I get into curve construction, I usually have a curve object, to which I add either points or curve segments. So I'd like to be able to do:
Curve3 curve;
curve.Add(float2()); // ambiguity
curve.Add(Array<float2>());
Unfortunately, I get an ambiguity here when I call add, because Add() will take either a float2 or an Array<float2>, which works fine, but an Array has the implicit constructor template <typename... Ts> Array(const Ts&...), which can take float2 as an argument. So the ambiguity is between
Array::Add(float2()); // and
Array::Add(Array<float2>(float2()));
I have tried making constructors that take arrays explicit, like
template <typename A, typename = std::enable_if<std::is_same<A, Array>::value, A>>
void Add(const Array& array);
But then I get new conversion errors from Curve3 to float2 etc. and it becomes a mess.
My hope is that somewhere in the depths of templates or other C++ goodies lies a simple solution that is just what I need. (Yes, I know that I can just rename the methods ::AddItem() and ::AddArray() and the problem will be over in a second, but I don't want this because eventually I want to double all this with += and then mostly just use that.
Any ideas?
Observe that you want
template <typename... Ts> Array(const Ts&... items);
to be used only if the parameter pack contains at least one item, and that item's type is not an Array template instance. If the parameter pack is empty, this becomes a default constructor, so let's handle this case separately. Go ahead and explicitly define a default constructor, if you need to, and have it do what it needs to do; now we can eliminate that possibility from this use case, and forge ahead.
Having gotten that out of the way, what you want to do here is to use this constructor only when it has one argument:
template <typename T1, typename... Ts> Array(const T1 &t1, const Ts&... items);
You will have to modify this constructor to use the explicit t1, in addition to the existing parameter pack it uses. That should be simple enough, but that won't be sufficient. There's still ambiguity. You want to have this constructor selected only if T1 is not an Array.
There's probably a way to come up with something convoluted and stuff it into a single std::enable_if, and shove it into this template. But for clarity, and simplicity, I would use a helper class:
template<typename T> class is_not_array : public std::true_type {};
template<typename T>
class is_not_array<Array<T>> : public std::false_type {};
And then add a simple std::enable_if into this constructor's template to use SFINAE to select this constructor only when its first template parameter is not an Array, similar to how you're using std::enable_if already, in the other constructor.
This should resolve all ambiguities. This constructor should then be picked only with at least one template parameter that's not an Array, with the assistance of this helper class. When it's an Array, this constructor will not be resolvable, and that case will go to the other constructor.
I would also suggest using universal references in the templates, instead of const T &s.
Related
I am trying to write a class template that uses a parameter-pack and implements a member function for each type contained in the parameter-pack.
This is what I have so far:
template <typename...T>
class Myclass {
public:
void doSomething((Some_Operator_to_divorce?) T) {
/*
* Do Something
*/
std::cout << "I did something" << std::endl;
}
};
My goal is to have a class template that can be used in the following way:
Myclass<std::string, int, double> M;
M.doSomething("I am a String");
M.doSomething(1234);
M.doSomething(0.1234);
Where the class template mechanism will create an implementation for a doSomething(std::string x), a doSomething(int x) and a doSomething(double x) member function but not a doSomething(std::string x, int i, double f) member function.
I found a lot of examples in the web on the usability of parameter-packs, but I could not figure out if it can be used for my purpose, or if I totally misunderstood for what a parameter-pack can be used.
I thought that I need to unpack the parameter-pack but, after reading a lot of examples about unpacking parameter packs, I believe that this is not the right choice and it has a complete different meaning.
So, therefore, I am looking for a operation to "divorce" a parameter-pack.
There is no "operator" specifically that supports this, but what you're requesting can be done in a few different ways, depending on your requirements.
The only way to "extract" T types from a parameter pack of a class template with the purpose of implementing an overload-set of functions is to implement it using recursive inheritance, where each instance extracts one "T" type and implements the function, passing the rest on to the next implementation.
Something like:
// Extract first 'T', pass on 'Rest' to next type
template <typename T, typename...Rest>
class MyClassImpl : public MyClassImpl<Rest...>
{
public:
void doSomething(const T&) { ... }
using MyClassImpl<Rest...>::doSomething;
};
template <typename T>
class MyClassImpl<T> // end-case, no more 'Rest'
{
public:
void doSomething(const T&) { ... }
};
template <typename...Types>
class MyClass : public MyClassImpl<Types...>
{
public:
using MyClassImpl<Types...>::doSomething;
...
};
This will instantiate sizeof...(Types) class templates, where each one defines an overload for each T type.
This ensures that you get overload semantics -- such that passing an int can call a long overload, or will be ambiguous if there are two competing conversions.
However, if this is not necessary, then it'd be easier to enable the function with SFINAE using enable_if and a condition.
For exact comparisons, you could create an is_one_of trait that only ensures this exists if T is exactly one of the types. In C++17, this could be done with std::disjunction and std::is_same:
#include <type_traits>
// A trait to check that T is one of 'Types...'
template <typename T, typename...Types>
struct is_one_of : std::disjunction<std::is_same<T,Types>...>{};
Alternatively, you may want this to only work if it may work with convertible types -- which you might do something like:
template <typename T, typename...Types>
struct is_convertible_to_one_of : std::disjunction<std::is_convertible<T,Types>...>{};
The difference between the two is that if you passed a string literal to a MyClass<std::string>, it will work with the second option since it's convertible, but not the first option since it's exact. The deduced T type from the template will also be different, with the former being exactly one of Types..., and the latter being convertible (again, T may be const char*, but Types... may only contain std::string)
To work this together into your MyClass template, you just need to enable the condition with SFINAE using enable_if:
template <typename...Types>
class MyClass
{
public:
// only instantiates if 'T' is exactly one of 'Types...'
template <typename T, typename = std::enable_if_t<is_one_of<T, Types...>::value>>
void doSomething(const T&) { ... }
// or
// only instantiate if T is convertible to one of 'Types...'
template <typename T, typename = std::enable_if_t<is_convertible_to_one_of<T, Types...>::value>>
void doSomething(const T&) { ... }
};
Which solution works for you depends entirely on your requirements (overload semantics, exact calling convension, or conversion calling convension)
Edit: if you really wanted to get complex, you can also merge the two approaches... Make a type trait to determine what type would be called from an overload, and use this to construct a function template of a specific underlying type.
This is similar to how variant needs to be implemented, since it has a U constructor that considers all types as an overload set:
// create an overload set of all functions, and return a unique index for
// each return type
template <std::size_t I, typename...Types>
struct overload_set_impl;
template <std::size_t I, typename T0, typename...Types>
struct overload_set_impl<I,T0,Types...>
: overload_set_impl<I+1,Types...>
{
using overload_set_impl<I+1,Types...>::operator();
std::integral_constant<std::size_t,I> operator()(T0);
};
template <typename...Types>
struct overload_set : overload_set_impl<0,Types...> {};
// get the index that would be returned from invoking all overloads with a T
template <typename T, typename...Types>
struct index_of_overload : decltype(std::declval<overload_set<Types...>>()(std::declval<T>())){};
// Get the element from the above test
template <typename T, typename...Types>
struct constructible_overload
: std::tuple_element<index_of_overload<T, Types...>::value, std::tuple<Types...>>{};
template <typename T, typename...Types>
using constructible_overload_t
= typename constructible_overload<T, Types...>::type;
And then use this with the second approach of having a function template:
template <typename...Types>
class MyClass {
public:
// still accept any type that is convertible
template <typename T, typename = std::enable_if_t<is_convertible_to_one_of<T, Types...>::value>>
void doSomething(const T& v)
{
// converts to the specific overloaded type, and call it
using type = constructible_overload_t<T, Types...>;
doSomethingImpl<type>(v);
}
private:
template <typename T>
void doSomethingImpl(const T&) { ... }
This last approach does it two-phase; it uses the first SFINAE condition to ensure it can be converted, and then determines the appropriate type to treat it as and delegates it to the real (private) implementation.
This is much more complex, but can achieve the overload-like semantics without actually requiring recursive implementation in the type creating it.
I think the problem is fairly common, so there should be a known solution. I came up with one, but I'm not really satisfied, so I'm asking here, hoping someone can help.
Say I have a function, whose signature is
template<typename T>
void foo(const MyArray<const T>& x);
The const in the template parameter is to prevent me from changin the array content, since (for reasons beyond this question), the accessors ([] and ()) of MyArray<T> are always marked const, and return references to T (hence, the const ensure safety, since MyArray<T>::operator[] returns T&, while MyArray<const T>::operator[] returns const T&).
Great. However, templates with different template arguments are non related, so I can't bind a reference to MyClass<T> to a reference of MyClass<const T>, meaning I can't do this
MyArray<double> ar(/*blah*/);
foo(ar);
Notice that, without a reference, the code above would work provided that there is a copy constructor that lets me create MyArray<const T> from MyArray<T>. However, I don't want to remove the reference, since the array construction would happen a lot of times, and, despite relatively cheap, its cost would add up.
So the question: how can I call foo with an MyArray<T>?
My only solution so far is the following:
MyArray<T> ar(/*blah*/);
foo(reinterpret_cast<MyArray<const T>>(ar));
(actually in my code I hid the reinterpret cast in an inlined function with more verbose name, but the end game is the same). The class MyArray does not have a specialization for const T that makes it not reinterpretable, so the cast should be 'safe'. But this is not really a nice solution to read. An alternative, would be to duplicate foo's signature, to have a version taking MyArray<T>, which implementation does the cast and calls the const version. The problem with this is code duplication (and I have quite a few functions foo that need to be duplicated).
Perhaps some extra template magic on the signature of foo? The goal is to pass both MyArray<T> and MyArray<const T>, while still retaining const-correctness (i.e., make the compiler bark if I accidentally change the input in the function body).
Edit 1: The class MyArray (whose implementation is not under my control), has const accessors, since it stores pointers. So calling v[3] will modify the values in the array, but not the members stored in the class (namely a pointer and some smart-pointer-like metadata). In other words, the object is de facto not modified by accessors, though the array is. It's a semantic distinction. Not sure why they went this direction (I have an idea, but too long to explain).
Edit 2: I accepted one of the two answers (though they were somewhat similar). I am not sure (for reasons long to explain) that the wrapper class is doable in my case (maybe, I have to think about it). I am also puzzled by the fact that, while
template<typename T>
void foo(const MyArray<const T>& x);
MyArray<int> a;
foo(a);
does not compile, the following does
void foo(const MyArray<const int>& x);
MyArray<int> a;
foo(a);
Note: MyArray does offer a templated "copy constructor" with signature
template<typename S>
MyArray(const MyArray<S>&);
so it can create MyArray<const T> from MyArray<T>. I am puzzled why it works when T is explicit, while it doesn't if T is a template parameter.
I would stay with
template<typename T>
void foo(const MyArray<T>&);
and make sure to instantiate it with const T (in unitTest for example).
Else you can create a view as std::span.
Something like (Depending of other methods provided by MyArray, you probably can do a better const view. I currently only used operator[]):
template <typename T>
struct MyArrayConstView
{
MyArrayConstView(MyArray<T>& array) : mArray(std::ref(array)) {}
MyArrayConstView(MyArray<const T>& array) : mArray(std::ref(array)) {}
const T& operator[](std::size_t i) {
return std::visit([i](const auto& a) -> const T& { return a[i]; }), mArray);
}
private:
std::variant<std::reference_wrapper<MyArray<T>>,
std::reference_wrapper<MyArray<const T>>> mArray;
};
and then
template <typename T>
void foo(const MyArrayConstView<T>&);
but you need to call it explicitly, (as deduction won't happen as MyArray<T> is not a MyArrayConstView)
MyArray<double> ar(/*blah*/);
foo(MyArrayConstView{ar});
foo<double>(ar);
Since you are not allowed to change MyArray, one option is to use an adapter class.
template <typename T>
class ConstMyArrayView {
public:
// Not an explicit constructor!
ConstMyArrayView(const MyArray<T>& a) : a_(a) {}
const T& operator[](size_t i) const { return a_[i]; }
private:
const MyArray<T>& a_;
};
template<typename T>
void foo(const ConstMyArrayView<T>& x);
MyArray<T> x;
foo(x);
But in the end, if you can change MyArray to match the const-correctness you want, or switch to a class that does, that'll be the better option.
Here's an ugly but effective way to have a function use one type, but also get the compiler to check that the same code would compile if it used a different type instead:
template <typename From, typename To>
struct xfer_refs_cv
{
using type = To;
};
template <typename From, typename To>
struct xfer_refs_cv<const From, To>
{
using type = const typename xfer_refs_cv<From, To>::type;
};
template <typename From, typename To>
struct xfer_refs_cv<volatile From, To>
{
using type = volatile typename xfer_refs_cv<From, To>::type;
};
template <typename From, typename To>
struct xfer_refs_cv<From&, To>
{
using type = typename xfer_refs_cv<From, To>::type&;
};
template <typename From, typename To>
struct xfer_refs_cv<From&&, To>
{
using type = typename xfer_refs_cv<From, To>::type&&;
};
template <typename CheckType, typename Func, typename CallType>
constexpr decltype(auto) check_and_call(Func&& f, CallType&& call_arg)
noexcept(noexcept(std::forward<Func>(f)(std::forward<CallType>(call_arg))))
{
(void) decltype(std::declval<Func&&>()
(std::declval<typename xfer_refs_cv<CallType&&, CheckType>::type>()), 0){};
return std::forward<Func>(f)(std::forward<CallType>(call_arg));
}
template<typename T>
void foo(const MyArray<T>& x)
{
check_and_call<MyArray<const T>>(
[](auto&& x) {
// Function implementation here.
}, x);
}
A class template like this
template <typename... T>
class Action {
private:
std::tuple<T...> m_args;
public:
Action(T... args) : m_args(args...) {}
}
to a class with template member. The reason why doing this is want to make only one type of class, so that object with different args still belong to the same class for easy manipulation.
class Action {
private:
// this does not work, how to declare the tuple type so that It can hold any arguments list.
template <typename... T>
std::tuple<T...> m_args;
public:
template <typename... T>
Action(T... args) : m_args(args...) {}
}
Put your mind in the perspective of the compiler. How would our trusty friend know how much storage is required for Action, if that size would be dependent upon what constructor was resolved? This is not possible.
template<typename... T>
Action(T... args) : m_args(args...) {}
// wait what???
Action(int someArg) : m_args(someArg) {}
Let's say the second constructor was valid, or we had two Action objects with different arguments passed into the constructor -- what should be sizeof(Action)?
If you get stuck on an issue like this, think as the compiler: there was probably some person who had to give a good enough reason why it shouldn't be supported, simply because it only complicates the implementation and also has performance implications.
Since you mentioned using a pointer instead of an aggregated object, I figured I'd show you how to do this for completeness. Do note that this has performance implications, because you are now allocating the memory for m_args on the heap, rather than the stack.
class Action
{
public:
template<typename... T>
using TArgs = std::tuple<T...>;
std::shared_ptr<void> m_args;
template<typename... T>
Action(T... args)
: m_args(std::make_shared<TArgs<T...>>(args...)) {}
};
Action myAction(1, 2.0, 3.5f);
auto *myActionArgs =
static_cast<Action::TArgs<int, double, float>*>(myAction.m_args.get());
// 2.0
double secondArg = std::get<1>(*myActionArgs);
This does not look like fun to maintain.
How to define method signature so it will accept same number of arguments as variadic template class definition? For example how to define an Array class:
template<typename T, int... shape>
class Array
{
public:
T& operator () (???);
};
So you will be able to call it like this:
Array<int, 3, 4, 5> a;
a(1, 2, 3) = 2;
template<class T, int...Shape>
class Array {
template<int>using index_t=int; // can change this
public:
T& operator()(index_t<Shape>... is);
};
or:
template<class T, int...Shape>
class Array {
public:
T& operator()(decltype(Shape)... is);
};
or:
template<class T, int...Shape>
class Array {
public:
T& operator()(decltype(Shape, int())... is);
};
if you want to be able to change the type of the parameter to be different than Shape.
I find the decltype harder to understand a touch than the using, especially if you want to change the type of the parameter to be different than int.
Another approach:
template<class T, int...Shape>
class Array {
public:
template<class...Args,class=typename std::enable_if<sizeof...(Args)==sizeof...(Shape)>::type>
T& operator()(Args&&... is);
};
which uses SFINAE. It does not enforce that the Args are integer types however. We could add another clause if we wanted to (that all of the Args are convertible to int, say).
Yet another approach is to have your operator() take a package of values, like a std::array<sizeof...(Shape), int>. Callers would have to:
Array<double, 3,2,1> arr;
arr({0,0,0});
use a set of {}s.
A final approach would be:
template<class T, int...Shape>
class Array {
public:
template<class...Args>
auto operator()(Args&&... is) {
static_assert( sizeof...(Args)==sizeof...(Shapes), "wrong number of array indexes" );
}
};
where we accept anything, then generate errors if it is the wrong number of arguments. This generates very clean errors, but does not do proper SFINAE operator overloading.
I would recommend tag dispatching, but I don't see a way to make it much cleaner than the SFINAE solution, with the extra decltype and all, or better error messages than the static_assert version on the other hand.
I assume you want your arguments to be all of the same type, probably using an integer type (I'll just use int). An easy approach is to leverage the parameter pack you already have:
template <int>
struct shape_helper { typedef int type; };
template <typename T, int... Shape>
class Array
{
public:
T& operator()(typename shape_helper<Shape>::type...);
};
I have a templated class
Vector<class T, int N>
Where T is the type of the components (double for example) and n the number of components (so N=3 for a 3D vector)
Now I want to write a method like
double findStepsize(Vector<double,2> v)
{..}
I want to do this also for three and higher dimensional vectors. Of course I could just introduce further methods for higher dimensions, but the methods would have a lot of redundant code, so I want a more generic solution. Is there a way to create a method which takes a templated class without further specializing it (in this case without specifying T or N)? Like
double findStepsize(Vector<T,N> v)
?
Yes it is
template<typename T, int N>
double findStepsize(Vector<T,N> v)
{..}
If you call it with a specific Vector<T, N>, the compiler will deduce T and N to the appropriate values.
Vector<int, 2> v;
// ... fill ...
findStepsize(v); /* works */
The above value-parameter matches your example, but it's better to pass user defined classes that need to do work in their copy constructors by const reference (Vector<T, N> const& instead). So you avoid copies, but still can't change the caller's argument.
Implement it this way:
template <typename A, int B>
class Vector {
};
template <typename T, int N>
void foo(Vector<T, N>& v) {
}
template <>
void foo(Vector<int, 3>& v) {
// your specialization
}
template <typename T, size_t N>
T find_step_size( const Vector<T,N>& v )
{
return T(); // or something
}
Your second question answer:
You can't have a templated pointer to function, that makes no sense.
But what you can do is
#include <vector>
template <typename T>
void foo(const std::vector<T>& v) {
// do something
}
void (*ptr_foo)(const std::vector<int>&) = &foo<int>;
(here the function pointers a templated function, which template argument is explicitly set to int)