Structs, templates use, and inheritance questions - c++

I recently came across a script(below) in which I do not understand why we need to have structs in this script and why they have to inherit false/true types. I also do not understand why "template" is repeated.
#include <iostream>
#include <vector>
using namespace std;
template<typename T>
struct is_vector: false_type {}; //<- why does the struct have to inherit something and what does it do?
template<typename T, typename Alloc>
struct is_vector<vector<T, Alloc>> : true_type {};
template<class T>
void func(const vector<T> & vec){
for(const auto & x : vec)
if constexpr (is_vector<T>::value)
func(x);
else
cout << x << ' ';
}
int main(){
func(vector<int>{5,10,15});
func(vector<vector<int>>{{5,10,15},{20,25,30}});
}

Related

Template constructor defined in the same header but outside the class is unidentified

I know template class definitions should be defined in the header file. However, the constructor defined in the header but outside the class seems to be unnoticed by the compiler while the constructor defined inside the class works just fine. Am I missing something completely obvious?
This works
#include <type_traits>
#include <iostream>
struct A {
template<typename T,
typename = typename std::enable_if<std::is_integral<T>::value>::type>
A(T t) {
this->t = static_cast<double>(t);
}
double t;
};
int main() {
A a(3);
std::cout << a.t << '\n';
}
This does not
#include <type_traits>
#include <iostream>
struct B {
template<typename T, typename> B(T t);
double t;
};
template<typename T,
typename = typename std::enable_if<std::is_integral<T>::value>::type>
B::B(T t) {
this->t = static_cast<double>(t);
}
int main() {
B b(3);
std::cout << b.t << '\n';
}
saying
error: no matching function for call to 'B::B(int)'
B b(3);
^
You forgot a few things.
The defaulted template argument should be in the class declaration.
And you'll need a typename added to that.
#include <type_traits>
#include <iostream>
struct B {
template<typename T, typename = typename std::enable_if<std::is_integral<T>::value>::type> B(T t);
double t;
};
template<typename T,
typename>
B::B(T t) {
this->t = static_cast<double>(t);
}
int main() {
B b(3);
std::cout << b.t << '\n';
}
Yeah I think this is just a gcc bug. Filed 88864.
This is a shorter reproduction that should compile but doesn't (clang accepts):
struct B {
template<typename T, typename U> B(T t);
};
template <typename T, typename U = int>
B::B(T t) { }
B b(3);

c++03: Mutually exclusive methods thanks to enable_if

Within a class, I have two different methods which should be mutually exclusive depending on the caller template parameter.
class Foo
{
// For collections
template<class T>
typename boost::enable_if<boost::is_same<typename std::vector<typename T::value_type>, T>::value, const T&>::type
doSomething()
{ }
// For single types
template<class T>
typename boost::enable_if<!boost::is_same<typename std::vector<typename T::value_type>, T>::value, const T&>::type
doSomething()
{ }
}
This won't compile.
error: type/value mismatch at argument 1 in template parameter list for 'template struct boost::enable_if'
error: expected a type, got '! boost::is_same::value'
How about:
template <typename T> struct is_std_vector : std::false_type {};
template <typename T, typename A>
struct is_std_vector<std::vector<T, A>> : std::true_type {};
And then
class Foo
{
// For collections
template<class T>
typename std::enable_if<is_std_vector<T>::value, const T&>::type
doSomething();
// For single types
template<class T>
typename std::enable_if<!is_std_vector<T>::value, const T&>::type
doSomething();
};
Unlike std's version, boost::enable_if accepts a type (kinda wrapper under boolean value), so you should write something like
class Foo
{
// For collections
template<class T>
typename boost::enable_if<
typename boost::is_same<typename std::vector<typename T::value_type>, T>,
const T&>::type doSomething()
{ }
// For single types
template<class T>
typename boost::enable_if_с<
!boost::is_same<typename std::vector<typename T::value_type>, T>::value,
const T&>::type doSomething()
{ }
}
Note here, I've used typename before boost::is_same and haven't used ::value in the first specification. On the contrary, I had to use enable_if_с in the second overload, because ! operator isn't applicable to a type.
What about a sort of tag dispatching?
#include <vector>
#include <iostream>
template <typename, typename>
struct isSame
{ typedef int type; };
template <typename T>
struct isSame<T, T>
{ typedef long type; };
struct foo
{
template <typename T>
T const & doSomething (T const & t, int)
{ std::cout << "int version" << std::endl; return t; }
template <typename T>
T const & doSomething (T const & t, long)
{ std::cout << "long version" << std::endl; return t; }
template <typename T>
T const & doSomething (T const & t)
{ return doSomething(t, typename isSame<
typename std::vector<typename T::value_type>, T>::type()); }
};
int main ()
{
foo f;
std::vector<int> v;
f.doSomething(v); // print "long version"
}
If what you want is to overload the function based on whether you are given a vector or not
#include <type_traits>
#include <iostream>
#include <vector>
using std::cout;
using std::endl;
class Foo {
public:
// For collections
template <class T>
const vector<T>& do_something(const std::vector<T>& input) {
cout << __PRETTY_FUNCTION__ << endl;
return input;
}
// For single types
template <class T>
const T& do_something(const T& input) {
cout << __PRETTY_FUNCTION__ << endl;
return input;
}
};
int main() {
auto foo = Foo{};
auto v = std::vector<int>{};
auto i = int{};
foo.do_something(v);
foo.do_something(i);
}
If you want to be even more general and check for any instantiated type
#include <type_traits>
#include <iostream>
#include <vector>
using std::cout;
using std::endl;
namespace {
template <typename T, template <typename...> class TT>
struct IsInstantiationOf
: public std::integral_constant<bool, false> {};
template <template <typename...> class TT, typename... Args>
struct IsInstantiationOf<TT<Args...>, TT>
: public std::integral_constant<bool, true> {};
} // namespace anonymous
class Foo {
public:
// For collections
template <typename VectorType, typename std::enable_if_t<IsInstantiationOf<
std::decay_t<VectorType>, std::vector>::value>* = nullptr>
void do_something(VectorType&&) {
cout << "Vector overload" << endl;
}
// For single types
template <class T, typename std::enable_if_t<!IsInstantiationOf<
std::decay_t<T>, std::vector>::value>* = nullptr>
void do_something(T&&) {
cout << "Non vector overload" << endl;
}
};
int main() {
auto foo = Foo{};
auto v = std::vector<int>{};
auto i = int{};
foo.do_something(v);
foo.do_something(i);
}
Also please note that you should avoid putting std::enable_if in the function signature as much as possible for these reasons https://stackoverflow.com/a/14623831/5501675

Check if a template argument is a std::vector<T>::iterator

How to check if a template argument is a std::vector<T>::iterator?
For void type, we have std::is_void. Is there something like that for std::vector<T>::iterator?
You could create a trait for that:
#include <vector>
#include <list>
#include <type_traits>
template <class T, class = void>
struct is_vector_iterator: std::is_same<T, std::vector<bool>::iterator> { };
template <class T>
struct is_vector_iterator<T, decltype(*std::declval<T>(), std::enable_if_t<!std::is_same<T, std::vector<bool>::iterator>::value>())>: std::is_same<T, typename std::vector<std::decay_t<decltype(*std::declval<T>())>>::iterator> { };
int main() {
static_assert(is_vector_iterator<std::vector<int>::iterator>::value, "Is not a vector iterator");
static_assert(is_vector_iterator<std::vector<bool>::iterator>::value, "Is not a vector iterator");
static_assert(!is_vector_iterator<std::list<int>::iterator>::value, "Is a vector iterator");
static_assert(!is_vector_iterator<std::list<int>::iterator>::value, "Is a vector iterator");
}
[live demo]
An alternative solution also using std::iterator_traits:
#include <iostream>
#include <vector>
#include <list>
template <typename T>
struct is_vector_iterator
{
typedef char yes[1];
typedef char no[2];
template <typename C>
static yes& test(
typename std::enable_if<
std::is_same<T, typename std::vector<typename C::value_type>::iterator>::value
>::type*);
template <typename>
static no& test(...);
static const bool value = sizeof(test<T>(nullptr)) == sizeof(yes);
};
int main() {
std::cout << is_vector_iterator<int>::value << std::endl;
std::cout << is_vector_iterator<int*>::value << std::endl;
std::cout << is_vector_iterator<std::list<int>::iterator>::value << std::endl;
std::cout << is_vector_iterator<std::vector<int>::iterator>::value << std::endl;
return 0;
}
live demo
Better using std::iterator_traits I think:
#include <list>
#include <vector>
#include <iterator>
template <class It, class = void>
struct is_vector_iterator : std::false_type { };
template <class It>
struct is_vector_iterator<It, std::enable_if_t<
std::is_same<
It,
typename std::vector<
typename std::iterator_traits<It>::value_type
>::iterator
>::value
>> : std::true_type { };
int main() {
static_assert(is_vector_iterator<std::vector<int>::iterator>::value, "Is not a vector iterator");
static_assert(is_vector_iterator<std::vector<bool>::iterator>::value, "Is not a vector iterator");
static_assert(!is_vector_iterator<std::list<int>::iterator>::value, "Is a vector iterator");
}
DEMO
You can write a trait for this:
namespace detail
{
template<typename T> constexpr std::false_type is_vector_iterator(T&&, ...)
{
return {};
}
template<typename T>
constexpr auto is_vector_iterator(T&& t, void* = nullptr) ->
decltype(std::is_same<typename std::vector<std::decay_t<decltype(*t)>>::iterator, std::decay_t<T>>{})
{
return {};
}
}
template<typename T>
struct is_vector_iterator : decltype(detail::is_vector_iterator(declval<T>(), 0)) {};
Here I'm getting the decayed type of *t to make vector<type>::iterator and check that for equality with T. It works with the exception of vector<bool> which isn't really a vector anyway.
demo

Check at compile-time is a template type a vector

I can imagine the following code:
template <typename T> class X
{
public:
T container;
void foo()
{
if(is_vector(T))
container.push_back(Z);
else
container.insert(Z);
}
}
// somewhere else...
X<std::vector<sth>> abc;
abc.foo();
How to write it, to successfully compile? I know type traits, but when I'm defining:
template<typename T> struct is_vector : public std::false_type {};
template<typename T, typename A>
struct is_vector<std::vector<T, A>> : public std::true_type {};
It doesn't compile:
error: no matching function for call to 'std::vector<sth>::insert(Z)'
static_assert also isn't that what I'm looking for. Any advices?
Here's a short example of what I want to achieve (SSCCE): http://ideone.com/D3vBph
It is named tag dispatching :
#include <vector>
#include <set>
#include <type_traits>
template<typename T> struct is_vector : public std::false_type {};
template<typename T, typename A>
struct is_vector<std::vector<T, A>> : public std::true_type {};
template <typename T>
class X {
T container;
void foo( std::true_type ) {
container.push_back(0);
}
void foo( std::false_type ) {
container.insert(0);
}
public:
void foo() {
foo( is_vector<T>{} );
}
};
// somewhere else...
int main() {
X<std::vector<int>> abc;
abc.foo();
X<std::set<int>> def;
def.foo();
}
An alternative worth considering is to detect the presence of the push_back function using SFINAE. This is slightly more generic since it'll translate to other containers that implement push_back.
template<typename T>
struct has_push_back
{
template<typename U>
static std::true_type test(
decltype((void(U::*)(const typename U::value_type&)) &U::push_back)*);
template<typename>
static std::false_type test(...);
typedef decltype(test<T>(0)) type;
static constexpr bool value =
std::is_same<type, std::true_type>::value;
};
Note that it currently only detects push_back(const T&) and not push_back(T&&). Detecting both is a little more complicated.
Here's how you make use of it to actually do the insert.
template<typename C, typename T>
void push_back_impl(C& cont, const T& value, std::true_type) {
cont.push_back(value);
}
template<typename C, typename T>
void push_back_impl(C& cont, const T& value, std::false_type) {
cont.insert(value);
}
template<typename C, typename T>
void push_back(C& cont, const T& value) {
push_back_impl(cont, value, has_push_back<C>::type());
}
std::vector<int> v;
push_back(v, 1);
std::set<int> s;
push_back(s, 1);
Honestly, this solution became a lot more complicated then I originally anticipated so I wouldn't use this unless you really need it. While it's not too hard to support const T& and T&&, it's even more arcane code that you have to maintain which is probably not worth it in most cases.
Using insert only:
#include <iostream>
#include <vector>
#include <set>
template <typename T>
class X
{
public:
T container;
template <typename U>
void insert(const U& u) {
container.insert(container.end(), u);
}
};
int main() {
X<std::vector<int>> v;
v.insert(2);
v.insert(1);
v.insert(0);
for(std::vector<int>::const_iterator pos = v.container.begin();
pos != v.container.end();
++pos)
{
std::cout << *pos;
}
std::cout << '\n';
X<std::set<int>> s;
s.insert(2);
s.insert(1);
s.insert(0);
for(std::set<int>::const_iterator pos = s.container.begin();
pos != s.container.end();
++pos)
{
std::cout << *pos;
}
std::cout << '\n';
}
Here's the typical method using void_t:
template <typename T>
using void_t = void; // C++17 std::void_t
template <typename C, typename = void> // I'm using C for "container" instead of T, but whatever.
struct has_push_back_impl : std::false_type {};
template <typename C>
struct has_push_back_impl<C, void_t<decltype(std::declval<C>().push_back(typename C::value_type{}))>>
: std::true_type {}; // Note that void_t is technically not needed in this case, since the 'push_back' member function actually returns void anyway, but it the general method to pass the type into void_t's template argument to obtain void. For example, the 'insert' function from std::set and std::map do NOT return void, so 'has_insert' will need to use void_t.
template <typename C>
using has_push_back = has_push_back_impl<C>; // void passed to the second template argument by default, thus allowing the second specialization to be used instead of the primary template whenever C has a push_back member function.
This method will work for has_insert for associative containers, even though std::set, std::map's insert function return std::pair<typename T::iterator, bool> while std::multimap::insert returns std::multimap::iterator (this is one case where Ze Blob's method will not work).
If you use constexpr if, you were doing it right. This C++17 code compiles:
#include <iostream>
#include <type_traits>
#include <vector>
#include <list>
template<typename T> struct is_vector : public std::false_type {};
template<typename T, typename A>
struct is_vector<std::vector<T, A>> : public std::true_type {};
template <typename T>
class X
{
public:
T container;
void foo()
{
if constexpr(is_vector<T>::value){
std::cout << "I am manipulating a vector" << std::endl;
// Can access container.push_back here without compilation error
}
else {
std::cout << "I am manipulating something else" << std::endl;
}
}
};
int main() {
X<std::vector<int>> abc;
abc.foo(); // outputs "I am manipulating a vector"
X<std::list<int>> def;
def.foo(); // outputs "I am manipulating something else"
}
in C++20 using requires expression:
#include <type_traits>
#include <concepts>
#include <vector>
template<class T>
static constexpr bool is_vector_v = requires {
requires std::same_as<std::decay_t<T>,
std::vector<typename std::decay_t<T>::value_type> >;
};
and in code:
template<class T>
void foo() {
if constexpr (is_vector_v<T>)
container.push_back(Z);
else
container.insert(Z);
}

type mapping by templates

I'm looking for a way to map types, f.i. having a class Double:
class Double
{
public:
typedef double basic_type;
...
};
I'd like to be able to have a type caster so that
typeid(TypeToObjectType<double>::type) == typeid(Double)
Any ideas how to accomplish this (through partial specializations etc.) ?
You can achieve this through specialization :
template<class T>
struct TypeToObjectType;
template<>
struct TypeToObjectType<double> {
typedef Double type;
};
Note that you have to provide a specialization for each of the types on which you want TypeToObjectType to work. Macros can be helpful here :
#define SPECIALIZE_TYPETOOBJECTTYPE(ObjectType) \
template<> struct TypeToObjectType<ObjectType::basic_type> { \
typedef ObjectType type; \
};
SPECIALIZE_TYPETOOBJECTTYPE(Int)
SPECIALIZE_TYPETOOBJECTTYPE(Double)
Sounds like you are looking for something like this:
template<typename T>
struct TypeToObjectType;
// specialization for T=double
template<>
struct TypeToObjectType<double> {
typedef Double type;
};
Here TypeToObjectType<double>::type is Double and you can add other specializations for additional mappings.
This should work very well, however, I haven't tested this method for classes of mine. I have used an idea from the book "C++ Templates: The Complete Guide", paragraph 20.4.2
#include <iostream>
#include <string>
#include <list>
#include <type_traits>
template<typename... Ts>
struct type_mapper;
template<>
struct type_mapper<>
{
static void mapTo(...);
};
template<typename T, typename... Ts>
struct type_mapper<T, Ts...> : type_mapper<Ts...>
{
static typename T::second_type mapTo(typename T::first_type);
using type_mapper<Ts...>::mapTo;
};
template<typename T, typename MapperT>
struct GetTypeOnMap
{
using type = decltype(MapperT::mapTo(std::declval<T>()));
};
template<typename T, typename MapperT>
using get_type_on_mapping = typename GetTypeOnMap<T, MapperT>::type;
int main(void)
{
using mapper = type_mapper <
std::pair<int, double>,
std::pair<double, int>,
std::pair<float, std::string>>;
using shouldBeDouble = get_type_on_mapping<int, mapper>;
using shouldBeString = get_type_on_mapping<float, mapper>;
std::cout << shouldBeDouble{2.9};
std::cout << shouldBeString{"Hello"};
return 0;
}
EDIT:
Another one solution. More concise and flexible:
#include <complex>
#include <type_traits>
#include <cstdint>
#include <iostream>
#include <string>
template <typename V1, typename V2, typename T>
struct OnTypesEqual : std::bool_constant<std::is_same_v<V1, V2>> {
using type = T;
};
template <typename T>
struct default_type : std::true_type {
using type = T;
};
template<typename T>
using TypesMapper = typename std::disjunction<
OnTypesEqual<T, double, std::int8_t>,
OnTypesEqual<T, float, std::int16_t>,
OnTypesEqual<T, std::string, std::int32_t>,
OnTypesEqual<T, char, std::int64_t>,
default_type<void>
>::type;
int main()
{
std::cout << typeid(TypesMapper<double>).name() << std::endl;
std::cout << typeid(TypesMapper<float>).name() << std::endl;
std::cout << typeid(TypesMapper<std::string>).name() << std::endl;
std::cout << typeid(TypesMapper<char>).name() << std::endl;
return 0;
}