I'm confused why the below code gets rejected by the compiler. Some help please?
template<template<template<class> class> class Ptr,
template<class> class Container, class T>
inline void print(Ptr<Container<T>> l) {
std::cout << '[';
for (auto it = l->begin(); it != l->end() - 1; ++it) {
std::cout << *it << ", ";
}
std::cout << l->back() << ']' << std::endl;
}
I got confused of what works and what not in #RSahu answer and whether it solves the problem in general.
But, inspired by it, I get the following solution. Which simply exploits the fact that the class template class can be variadic. So for example is now compatible with std::vector<T, A> or other classes (containers hopefuly) that have more or less template parameters. In fact it is useful to do the same with the Ptr parameter so it is also compatible with std::unique_ptr that takes more template parameters than std::shared_ptr.
http://coliru.stacked-crooked.com/a/40d43526ed77eb9d
#include <iostream>
#include <vector>
#include <memory>
template<template<class...> class Ptr, template<class...> class Container, class T>
inline void f(Ptr<Container<T>> p) {
std::cout << "bla" << std::endl;
}
int main() {
f(std::make_shared<std::vector<int>>());
}
Note: having said that, I think there are better ways to constrain the argument types than forcing patterns.
For example, this could be an alternative:
template<class PtrContainerT, typename = decltype( std::declval<PtrContainerT>()->back())> //probably pretty much constrains the intended use
inline void f(PtrContainerT&& p) {
using T = typename PtrContainerT::element_type::value_type; //in case you need to know T
std::cout << "bla" << std::endl;
}
http://coliru.stacked-crooked.com/a/fbe43aab94364764
or even more to the point (although not 100% equivalent) :
template<class PtrContainerT, typename T = typename PtrContainerT::element_type::value_type> //probably pretty much constrains the intended use
inline void f(PtrContainerT&& p) {
std::cout << "bla" << std::endl;
}
Use std::decay<PtrContainerT>::...etc if necessary.
I can think of the following two ways to resolve the problem.
template <template <template <class> class> class Ptr,
template<class> class Container, class T>
inline void print(Ptr<Container> l) {
// ^^^ Not Ptr<Container<T>>
std::cout << '[';
for (auto it = l->begin(); it != l->end() - 1; ++it) {
std::cout << *it << ", ";
}
std::cout << l->back() << ']' << std::endl;
}
or
template <template<class> class Ptr,
// One less level of template for Ptr
template<class> class Container, class T>
inline void print(Ptr<Container<T>> l) {
// Containe<T> is a class
std::cout << '[';
for (auto it = l->begin(); it != l->end() - 1; ++it) {
std::cout << *it << ", ";
}
std::cout << l->back() << ']' << std::endl;
}
Looking at your code, it not clear which one will work for you.
Update
The following code does not work
#include <vector>
#include <memory>
template<template<class> class Ptr, template<class> class Container, class T>
inline void f(Ptr<Container<T>> p) {}
int main() {
f(std::make_shared<std::vector<int>>());
}
since std::vector is defined as:
template<
class T,
class Allocator = std::allocator<T>
> class vector;
It is not
template<
class T,
> class vector;
This works:
template<template<class> class Ptr, template<class> class Container, class T>
inline void f(Ptr<Container<T>> p) {}
template <typename T> using MyVector = std::vector<T>;
int main() {
f<std::shared_ptr, MyVector, int>(std::make_shared<MyVector<int>>());
}
This also works:
template<template<class> class Ptr, template<class> class Container, class T>
inline void f(Ptr<Container<T>> p) {}
template <typename T> struct Foo {};
int main() {
f(std::make_shared<Foo<int>>());
}
Related
I need to implement a template function that accepts any STL container. And based on what kind of container to perform certain actions.
Example:
template <class Container, class T>
void func(Container<T> container) {
if (container == std::map) {
...
} else {
...
}
}
int main() {
std::vector<int> v1;
func(v1); // ok
std::vector<double> v2;
func(v2); // ok
std::map<int, double> m1;
func(m1); // ok
std::list<int> l1;
func(l1); // ok
}
You can apply Constexpr If (since C++17) (with std::is_same) to check the type at compile-time, and apply parameter pack (since C++11) because these containers take multiple template parameters. e.g.
template <template <typename...> class Container, class... T>
void func(const Container<T...>& container) {
if constexpr (std::is_same_v<Container<T...>, std::map<T...>>) {
...
} else if constexpr (std::is_same_v<Container<T...>, std::vector<T...>>) {
...
} else if constexpr (std::is_same_v<Container<T...>, std::list<T...>>) {
...
} else {
...
}
}
PS: It depends on your intent but changing the parameter to pass-by-reference-to-const to avoid unnecessary copy might be a good idea.
Since you're making one implementation per container anyway, you could make overloads directly. It'll work in C++11 and has the benefit of having the template parameters easily available in each overload.
template <class T, size_t N>
void func(const std::array<T,N>& c) {
std::cout << "array " << c.size() << '\n';
}
template <class T, class Alloc>
void func(const std::vector<T,Alloc>& c) {
std::cout << "vector " << c.size() << '\n';
}
template <class T, class Alloc>
void func(const std::list<T,Alloc>& c) {
std::cout << "list " << c.size() << '\n';
}
template <class Key, class T, class Comp, class Alloc>
void func(const std::map<Key,T,Comp,Alloc>& c) {
std::cout << "map " << c.size() << '\n';
}
template <class CharT, class Traits, class Alloc>
void func(const std::basic_string<CharT,Traits,Alloc>& c) {
std::cout << "basic_string " << c.size() << '\n';
}
// add more of the containers you aim to support here
Consider the following declaration:
template <class T>
bool DoSomething(const T& value);
template <class D>
bool DoSomething(const std::vector<D>& value);
Is it possible to somehow unite this into single function declaration?
E.g. something like that:
template <class T, class D> bool DoSomething(...);
You code
template <class T>
bool DoSomething(const T& value);
is already accepting std::vector. If you want to do something in your DoSomething method which is different if T is a vector, then you can use this approach to check if the T is a specific type. Don't forget that templates are code generators.
Template parameters can be types, non-types, and templates.
And I suppose you are looking at something like this
#include <iostream>
#include <list>
#include <vector>
#include <string>
template <typename T, template <typename, typename> class Cont >
class Matrix{
public:
explicit Matrix(std::initializer_list<T> inList): data(inList){
for (auto d: data) std::cout << d << " ";
}
int getSize() const{
return data.size();
}
private:
Cont<T, std::allocator<T>> data;
};
int main(){
std::cout << std::endl;
Matrix<int, std::vector> myIntVec{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
std::cout << std::endl;
std::cout << "myIntVec.getSize(): " << myIntVec.getSize() << std::endl;
Matrix<std::string, std::list> myStringList{"one", "two", "three", "four"};
std::cout << std::endl;
std::cout << "myStringList.getSize(): " << myStringList.getSize() << std::endl;
std::cout << std::endl;
}
Matrix is a simple class template, that can be initialised by a std::initializer_list . A Matrix can be used with a std::vector , or a std::list to hold its values.
live Demo
Well, template <class T> bool DoSomething(const T& value); can also be calles with any vector. If it works depends on what you are trying to do. Templates are just code generators. So whatever class T is, it can be used as template paramter if it has all the required members.
For example the following would work:
#include <iostream>
#include <vector>
#include <string>
class MyContainer {
public:
size_t size() const
{
return 2;
}
int front() const
{
return 0;
}
int back() const
{
return 1;
}
};
template<class T>
void foo(const T& t)
{
if (t.size() >= 2)
{
std::cout << "(" << t.front() << ", " << t.back() << ")" << std::endl;
}
}
int main()
{
std::vector<std::string> stringVec{ "abc", "def" };
MyContainer cont;
foo(stringVec); // prints "(abc, def)"
foo(cont); // prints "(0, 1)"
}
That's because both MyContainer and std::vector<std::string> have all the methods that are uses in the template. Actually this code should work with almost all STL-Containers.
Short answer: Geneally you can't.
Honest answer: Yes, it can be done, depends on what you plan to do with argument. In abstract case you would need to generalize the function prototype, but still have two specializations of template using SFINAE, which makes three declarations instead of two.
Long answer: In some cases you can take advantage of if constexpr
#include <iostream>
#include <type_traits>
#include <vector>
template<typename Test, template<typename...> class Ref>
struct is_specialization : std::false_type {};
template<template<typename...> class Ref, typename... Args>
struct is_specialization<Ref<Args...>, Ref>: std::true_type {};
template <class T> bool DoSomething(const T& arg)
{
if constexpr(is_specialization<T, std::vector>::value)
{
return !arg.empty();
} else
{
return bool(arg);
}
}
int main()
{
std::vector<int> s;
std::cout << DoSomething(s) << std::endl;
std::cout << DoSomething(1) << std::endl;
}
I have some classes which need to define a template which can be used in generic code parts as type later.
In real world code the forwarded templates have a lot more parameters and it is not really nice to read the code.
Q: Is it possible to define the template in some syntax instead of writing it as alias template as given in the following example? I simple would avoid repeating of all the template parameters two times of each alias declaration.
The real world template also have some non type template parameters so simply using <PARMS...> will not work.
Example:
#include <iostream>
template < typename T>
struct A
{
static void Do(T t) { std::cout << "A " << t << std::endl;}
};
template < typename T>
struct B
{
static void Do(T t) { std::cout << "B " << t << std::endl;}
};
struct UseA
{
// using the alias template works as expected, but...
template < typename T>
using USE = A<T>;
// is there any chance to write something like:
// using USE = A;
// to simply avoid replication of template parameters?
};
struct UseB
{
template < typename T>
using USE = B<T>;
};
int main()
{
UseA::USE<int>::Do(1);
UseB::USE<std::string>::Do("Hallo");
}
What you are asking cannot be done. You always have to define the whole type list. The reason is, that one could have default overloads for the same type. For example, in the following A<int, 3>, A<int> and A<> are all valid. The compiler does not know which one you want:
template <class T, int Value = 42>
struct A {};
auto test() {
auto a = A<int, 3>{};
auto b = A<int>{};
auto c = A<>{};
}
If you don't want to write the type lists, I would recommend you to switch to templatizing more of your classes, so they don't need to know about the implementation details. Like:
#include <iostream>
template < typename T>
struct A
{
static void Do(T t) { std::cout << "A " << t << std::endl;}
};
template < typename T>
struct B
{
static void Do(T t) { std::cout << "B " << t << std::endl;}
};
template < typename T>
struct Use
{
using USE = T;
};
int main()
{
Use<A<int>>::USE::Do(1);
Use<B<std::string>>::USE::Do("Hallo");
}
Or alternatively, use containers for your non template type values:
#include <iostream>
template < int Value >
struct INT
{
static constexpr int value = Value;
};
template < bool Value >
struct BOOL
{
static constexpr bool value = Value;
};
template < typename T, typename Value >
struct A
{
static void Do(T t) { std::cout << "A " << t << Value::value << std::endl;}
};
template < typename T, typename Value>
struct B
{
static void Do(T t) { if (Value::value) std::cout << "B " << t << std::endl;}
};
template <template<typename...> class T, typename ...Param>
using USE = T<Param...>;
int main()
{
USE<A, int, INT<42>>::Do(1);
USE<B, std::string, BOOL<true>>::Do("Hallo");
}
I tried to implement template function specialization. You can run my tiny code in this fiddle. You can also see it below
#include <iostream>
#include <vector>
#include <list>
template <typename T>
struct is_vector {
static const bool value = false;
};
template <typename T>
struct is_vector<std::vector<T>> {
static const bool value = true;
using type = std::vector<T>;
};
template <typename T>
struct is_list {
static const bool value = false;
};
template <typename T>
struct is_list<std::list<T>> {
static const bool value = true;
using type = std::list<T>;
};
template<typename T, class = typename std::enable_if<is_list<T>::value>::type>
void foo(T t) {
std::cout << "is list" << std::endl;
}
/*
template<typename T, class = typename std::enable_if<is_vector<T>::value>::type>
void foo(T t) {
std::cout << "is vector" << std::endl;
}
*/
//The above code will cause an error, if we uncomment it
int main()
{
foo(std::list<int>{});
return 0;
}
In this code, I have several lines commented:
template<typename T, class = typename std::enable_if<is_vector<T>::value>::type>
void foo(T t) {
std::cout << "is vector" << std::endl;
}
If I uncomment it, I get "redifinition" error. I'm not sure how to fix it.
If I uncomment it, I get "redifinition" error. I'm not sure how to fix it.
The reason is quite simple: default values for template type arguments are not a part of a function signature. It means that you have the same template defined two times.
You might move SFINAE part in to the function return type, as it is suggested by other answers, or change the code to:
template<typename T, std::enable_if_t<is_list<T>::value, int> = 0>
void foo(T t) {
std::cout << "is list" << std::endl;
}
template<typename T, std::enable_if_t<is_vector<T>::value, int> = 1>
void foo(T t) {
std::cout << "is vector" << std::endl;
}
You can do this instead.
template<typename T>
typename std::enable_if<is_list<T>::value>::type foo(T t) {
std::cout << "is list" << std::endl;
}
template<typename T>
typename std::enable_if<is_vector<T>::value>::type foo(T t) {
std::cout << "is vector" << std::endl;
}
Not sure if this is what you are after, but you could just check if either list or vector is a matching type:
template<typename T, class = typename std::enable_if<is_list<T>::value || is_vector<T>::value>::type>
void foo(T t) {
std::cout << "is list" << std::endl;
}
Updated fiddle: https://godbolt.org/g/oD3o9q
Update (for C++14):
template<typename T, class = std::enable_if_t<is_list<T>::value || is_vector<T>::value>>
void foo(T t) {
std::cout << "is list" << std::endl;
}
I want to specialise a single template method in a non-template class to use an std::vector however only the return type of the method uses the template.
#include <iostream>
#include <string>
#include <vector>
class Foo
{
public:
template<typename T>
T Get()
{
std::cout << "generic" << std::endl;
return T();
}
};
template<>
int Foo::Get()
{
std::cout << "int" << std::endl;
return 12;
}
template<typename T>
std::vector<T> Foo::Get()
{
std::cout << "vector" << std::endl;
return std::vector<T>();
}
int main()
{
Foo foo;
auto s = foo.Get<std::string>();
auto i = foo.Get<int>();
}
This compiles with an error indicating that the std::vector attempted specialisation does not match any prototype of Foo, which is completely understandable.
In case it matters, use of C++14 is fine and dandy.
You can only partially specialize classes (structs) (cppreference) - so the way to overcome your problems is to add helper struct to allow this partial specialization of std::vector<T> - e.g. this way:
class Foo
{
private: // might be also protected or public, depending on your design
template<typename T>
struct GetImpl
{
T operator()()
{
std::cout << "generic" << std::endl;
return T();
}
};
public:
template<typename T>
auto Get()
{
return GetImpl<T>{}();
}
};
For int - you can fully specialize this function:
template<>
int Foo::GetImpl<int>::operator()()
{
std::cout << "int" << std::endl;
return 12;
}
For std::vector<T> you have to specialize entire struct:
template<typename T>
struct Foo::GetImpl<std::vector<T>>
{
std::vector<T> operator()()
{
std::cout << "vector" << std::endl;
return std::vector<T>();
}
};
Partial specialisation of template functions (including member functions) is not allowed. One option is to overload instead using SFINAE. For example,
/// auxiliary for is_std_vetor<> below
struct convertible_from_std::vector
{
template<typename T>
convertible_from_std::vector(std::vector<T> const&);
};
template<typename V>
using is_std_vector
= std::is_convertible<V,convertible_from_std_vector>;
class Foo
{
public:
template<typename T, std::enable_if_t< is_std::vector<T>::value,T>
Get()
{
std::cout << "vector" << std::endl;
return T();
}
template<typename T, std::enable_if_t<!is_std::vector<T>::value,T>
Get()
{
std::cout << "generic" << std::endl;
return T();
}
};
Note that the helper class is_std_vector may be useful in other contexts as well, so it worth having somewhere. Note further that you can make this helper class more versatile by asking for any std::vector or specific std::vector<specific_type, specific_allocator>. For example,
namespace traits {
struct Anytype {};
namespace details {
/// a class that is convertible form C<T,T>
/// if either T==AnyType, any type is possible
template<template<typename,typename> C, typename T1=Anytype,
typename T2=Anytype>
struct convCtTT
{
convCtTT(C<T1,T2> const&);
};
template<template<typename,typename> C, typename T1=Anytype>
struct convCtTT<C,T1,AnyType>
{
template<typename T2>
convCtTT(C<T1,T2> const&);
};
template<template<typename,typename> C, typename T2=Anytype>
struct convCtTT<C,AnyType,T2>
{
template<typename T1>
convCtTT(C<T1,T2> const&);
};
template<template<typename,typename> C>
struct convCtTT<C,AnyType,AnyType>
{
template<typename T1, typename T2>
convCtTT(C<T1,T2> const&);
};
}
template<typename Vector, typename ValueType=AnyType,
typename Allocator=AnyType>
using is_std_vector
= std::is_convertible<Vector,details::convCtTT<std::vector,ValueType,
Allocator>;
}
You can't partially specialze template in c++. You need to overload your function and pass the type in parameters.
#include <iostream>
#include <string>
#include <vector>
class Foo
{
public:
template<typename T>
T Get()
{
return this->getTemplate(static_cast<T*>(0)); //
}
private:
template<class T> T getTemplate(T* t)
{
std::cout << "generic" << std::endl;
return T();
}
template<class T> std::vector<T> getTemplate(std::vector<T>* t)
{
std::cout << "vector" << std::endl;
return std::vector<T>();
}
};
template <> int Foo::getTemplate(int* t)
{
std::cout << "int" << std::endl;
return 12;
}
int main()
{
Foo foo;
auto s = foo.Get<std::string>();
auto i = foo.Get<int>();
auto v = foo.Get<std::vector<int>>();
}
Edit : fixed a typo in the code