Template class specialization vs function overloading - c++

The title is not the best so let me explain: I'm experimenting with a custom mini-stl (for learning purposes) and currently implementing the std::distance function. The function must behave differently for random access iterators and simple input iterators.
Function overloading
Both std (in Microsoft's implementation) and EASTL use operator overloading to select the appropriate function, something like this:
namespace internal
{
template <typename Iter>
inline typename IteratorTraits<Iter>::DifferenceType DistanceImpl(Iter first, Iter last, InputIteratorTag)
{
// ...
}
template <typename Iter>
inline typename IteratorTraits<Iter>::DifferenceType DistanceImpl(Iter first, Iter last, RandomAccessIteratorTag)
{
// ...
}
}
template <typename Iter>
inline typename IteratorTraits<Iter>::DifferenceType Distance(Iter first, Iter last)
{
// the last parameter of the function selects the proper DistanceImpl
return internal::DistanceImpl<Iter>(first, last, (typename IteratorTraits<Iter>::IteratorCategory)());
}
I guess the temporary object (the category tag parameter which is an empty struct) will be optimized away since it's not used.
Class specialization with static function
What's about the specialization of a helper class with a static function?
namespace internal
{
template <typename Cat>
struct DistanceImpl;
template <>
struct DistanceImpl<InputIteratorTag>
{
template <typename Iter>
inline static typename IteratorTraits<Iter>::DifferenceType Calc(Iter first, Iter last)
{
// ...
}
};
template <>
struct DistanceImpl<RandomAccessIteratorTag>
{
template <typename Iter>
inline static typename IteratorTraits<Iter>::DifferenceType Calc(Iter first, Iter last)
{
// ...
}
};
}
template <typename Iter>
inline typename IteratorTraits<Iter>::DifferenceType Distance(Iter first, Iter last)
{
return internal::DistanceImpl<typename IteratorTraits<Iter>::IteratorCategory>::Calc<Iter>(first, last);
}
Questions
Difference(s) between the two solution? (Including performance and reliability)
Advantages/Disadvantages?

Tag dispatch automatically handles inheritance hierarchies; explicit specializations don't. This is particularly important with iterators, because the standard iterator category tags form an inheritance hierarchy: random_access_iterator_tag derives from bidirectional_iterator_tag which derives from forward_iterator_tag which derives from input_iterator_tag.
The first version works out of the box when given a forward or bidirectional iterator by selecting the input iterator overload. The second doesn't and requires additional specializations or some other changes.

Related

C++ Template function returning type based on template arguments

I'd like to create function that creates different types of generators (own classes) and I go with something like this:
template <typename Iterator>
class Generator {
Iterator begin_;
Iterator end_;
public:
Generator(Iterator begin, Iterator end)
: begin_(begin)
, end_(end)
{}
};
template <typename GeneratorType, typename ContainerIterator>
GeneratorType<ContainerIterator> make_generator(ContainerIterator begin, ContainerIterator end){ // Error occurs here
return GeneratorType<ContainerIterator>(std::forward<ContainerIterator>(begin), std::forward<ContainerIterator>(end));
}
But it's not compiling since error:
error: 'GeneratorType' is not a template (in line GeneratorType<ContainerIterator> make_generator...)
Do anyone know if it is possible and if yes how to fix it?
Change make_generator's definition
template <template<class> class GeneratorType, typename ContainerIterator>
GeneratorType<ContainerIterator> make_generator(ContainerIterator begin, ContainerIterator end)
You can pass templates as parameters to other templates. But the parameter definition needs to be specified as expecting a template template-parameter.
As the error message says, GeneratorType is a type, not a template. You need to make it a template template parameter using the template <typename> class syntax:
template <template <typename> class GeneratorType, typename ContainerIterator>
GeneratorType<ContainerIterator>
make_generator(ContainerIterator begin, ContainerIterator end){
return GeneratorType<ContainerIterator>
(std::forward<ContainerIterator>(begin),
std::forward<ContainerIterator>(end));
}

Compile-time check if iterator's parent class public member exists

I have a class:
class A
{
// ...
public:
std::string s;
// ...
}
And a function:
void some_process(RandomIt first, RandomIt last)
{
static_assert(/* some check if *RandomIt has member with name `s` */,
"Iterator must point to an object with member `s`");
// further process using RandomIt and using *RandomIt.s
}
How to implement this check in terms of C++ up to C++17?
c++11, c++14:
#include <type_traits>
#include <utility>
template <typename T, typename = void>
struct has_s : std::false_type {};
template <typename T>
struct has_s<T, decltype(void(std::declval<T>()->s))> : std::true_type {};
template <typename RandomIt>
void some_process(RandomIt first, RandomIt last)
{
static_assert(has_s<RandomIt>{},
"Iterator must point to an object with member `s`");
}
DEMO
c++20:
#include <type_traits>
#include <utility>
template <typename T>
using has_s_t = decltype(std::declval<T>()->s);
template <typename RandomIt>
void some_process(RandomIt first, RandomIt last)
{
static_assert(std::is_detected_v<has_s_t, RandomIt>,
"Iterator must point to an object with member `s`");
}
DEMO 2
One other potential C++1z option is Concepts. Here's a simple example of a concept that probably isn't useful on its own, but the idea can be used to make what you need for your situation.
template<typename T>
concept bool PointeeHasSMember = requires(T t)
{
t->s; // require t->s to be a valid expression
};
struct with_s
{
int s;
};
struct no_s {};
void some_process(PointeeHasSMember first, PointeeHasSMember last) {}
int main()
{
with_s* with;
no_s* without;
some_process(with, with); // compiles
some_process(without, without); // doesn't compile
}
Under the latest GCC, the second call produces an error with the relevant line concept 'PointeeHasSMember<no_s*>' was not satisfied' was not satisfied.
The advantage of using concepts is the simple implementation, even compared to the detection idiom, and that the concept becomes part of the function template. You have the flexibility of nesting requirements, doing on-the-fly requirements, and overloading on the concept. Your function declaration also clearly states its requirements instead of delaying that to a static assertion.

Disallow function template instantiation with iterator parameter

I have a function template which takes a templated parameter:
template <class R>
RefT<R> make_ref(R& res) {
return RefT<R>(&res);
}
I either want to prevent R from being any kind of iterator, or, if this is easier, I want to have a overload that the compiler will prefer to use for iterators which calls make_ref again with the iterator dereferenced.
Best approach would be combining the two, so the compiler prefers using iterator specific overload, and refuses to use the non-specific version.
I would like consumers of the code to be able to call make_ref(something) without having to think about whether the something is an iterator or not - I just need to do something different if it is, and if that's not possible, give a useful error message to the consumer.
First the traits (you may have to tweak it with your requirements):
template <typename T>
auto is_iterator_impl(T* it)
-> decltype(**it, ++(*it), (*it) == (*it), std::true_type());
template <typename T>
auto is_iterator_impl(...) -> std::false_type;
template <typename T>
using is_an_iterator = decltype(is_iterator_impl<T>(0));
Note: using std::iterator_traits<IT> may be a good alternative.
With SFINAE, you may do
template <class R>
std::enable_if_t<!is_an_iterator<R>::value, RefT<R>>
make_ref(R& res) {
return RefT<R>(&res);
}
template <class R>
std::enable_if_t<is_an_iterator<R>::value && !std::is_pointer<R>::value, RefT<R>> // you may want to change return type
make_ref(R& res) {
// Implementation for iterator
}
template <class R>
std::enable_if_t<std::is_pointer<R>::value, RefT<R>> // you may want to change return type
make_ref(R& res) {
// Implementation for iterator
}
Note: as you want to manage pointer differently, I also use std::is_pointer in addition to the custom is_an_iterator.
Note: The conditions should not have overlap, else you have conflict.
Live Demo
I used is_iterator from here: https://stackoverflow.com/a/4336298/678093
This traits struct is used with SFINAE to only enable make_ref for non-iterator types:
#include <type_traits>
template<class T>
struct is_iterator
{
static T makeT();
typedef void * twoptrs[2]; // sizeof(twoptrs) > sizeof(void *)
static twoptrs & test(...); // Common case
template<class R> static typename R::iterator_category * test(R); // Iterator
template<class R> static void * test(R *); // Pointer
static const bool value = sizeof(test(makeT())) == sizeof(void *);
};
// just to make it compile
template <typename R>
struct RefT{};
template <class R, typename std::enable_if<!is_iterator<R>::value>::type* = nullptr>
RefT<R> make_ref(R& res)
{
return RefT<R>(&res);
}
int main()
{
int* a;
make_ref(a); // fails to compile
int b;
make_ref(b); // compiles once RefT is correct
return 0;
}
An alernative solution is to use std::iterator_traits:
template <class R, typename std::enable_if<std::is_same<typename std::iterator_traits<R>::value_type, void>::value>::type* = nullptr>
RefT<R> make_ref(R& res)
{
return RefT<R>(&res);
}
This could also be done by using SFINAE with std::iterator_traits, would handle all cases that previous answers handle (pointers and types having internal iterator_category typedef) but:
no need to write your own traits (like is_iterator) to do this, or at least most of the template machinery is encapsulated in iterator_traits
could also handle potential user defined iterators that were having their own iterator_traits specialization without using the generic iterator_category typedef, not sure if this relevant/legal technique but definitely possible

How to deduce iterator template type, or its template's nested type?

This questions begs a more preparation, so I provide some bits of code first and then the exact question
Assuming I have the following type declared
template<typename T>
struct some_type
{
T t_;
};
which would be constructed with a factory function like so
typedef float numeric_type;
std::vector<std::string> construction_material;
//Push_back of strings in certain form...
std::vector<std::unique_ptr<some_type<numeric_type>> instances;
build_instances(construction_material.begin(), construction_material.end(), back_inserter(instances));
and the construction function would be something like following
template<typename input_iterator, typename output_iterator>
output_iterator build_instances(input_iterator begin, input_iterator end, output_iterator out)
{
for(input_iterator iter = begin; iter != end; ++iter)
{
//This won't work, but to illustrate some ideas...
//build_instance<std::iterator_traits<output_iterator>::value_type>(*iter)
}
//[...]
return *out;
}
template<typename T>
std::unique_ptr<some_type<T>> build_instance(std::string const& material)
{
static_assert(std::is_floating_point<T>::value == true, "The template type needs to be a floating point type.");
std::unique_ptr<some_instance<T>> instance(new some_instance<T>());
//Some processing...
return instance;
}
I know I could change the function to return some container (or perhaps even templatize the container type), like
template<typename input_iterator, typename T>
std::vector<std::unique_type<T>> build_instances(input_iterator begin, input_iterator end,
output_iterator out)
{
//Likewise code to the previous snippets...
return ...
}
The problems I haven't been able to solve are:
Would it be possible -- or impossible -- to use the back_inserter like approach? It looks like being the most flexible for the caller?
How to get a hold on the numeric_type in build_instances body (as in having it through output_iterator) so that it can be used in building the instances one-by-one?
How to ensure the caller knows to wait for the objects wrapped in std::unique_ptrs? An alternative would be just as plain pointers, but I'm not enthuasiastic about that.
There's a similar kind of question with a heading How can I make this template method more elegant? (or: less explicit template parameters required), which takes a container and transforms it to a different type of a container.
Edit
As commented to Jogojapan's comment that currently I transform the input like so
std::transform(construction_material.begin(), construction_material.end(), std::inserter(instances, instances.begin()), build_instance<numeric_type>);
but the subsequent function calls need to be supplied the numeric_type typedef too, which is somewhat cumbersome. I hope to avoid that. It looks I was mistaken, but for the purpose of education and all, would it be possible to further reduce the need to typedef the numeric type and deduce it from the iterator?
An intrusive solution is to let some_type exposes its type parameter, the same way std::unique_ptr<T, D> exposes its first parameter via element_type (which we will need later):
template<typename T>
struct some_type
{
// give it an appropriate meaningful name
using value_type = T;
value_type t_;
};
template<typename input_iterator, typename output_iterator>
output_iterator build_instances(input_iterator begin, input_iterator end, output_iterator out)
{
using pointer_type = typename std::iterator_traits<output_iterator>::value_type;
using value_type = typename pointer_type::element_type::value_type;
return std::transform(begin, end, out, build_instance<value_type>);
}
You can also non-intrusively extract the first template parameter of a template specialization:
template<typename T>
struct first;
template<template<typename...> class Template, typename First, typename... Pack>
struct first<Template<First, Pack...>>> {
using type = First;
};
template<typename T>
using First = typename first<T>::type;
The value_type alias in build_instances would instead become
using value_type = First<typename pointer_type::element_type>;
As a final remark I find it a bit odd that build_instance take a T parameter but constructs instances of some_type<T>. If it took T and constructed instances of T (where perhaps T is restricted to be a specialization of some_type.) This would have spared your problem, too.

enable_if iterator as a default template parameter?

I have a constructor like that :
class MyClass
{
template<class TI> MyClass(TI first, TI last);
};
template<class TI> MyClass::MyClass(TI first, TI last)
{
;
}
I would like to enable this constructor only if TI is an iterator (that means TI has an iterator_category I think). How to do that in C++ 2011 using an enable_if as a default template parameter (in the declaration and in the definition) ?
Thank you very much.
It depends on what you want. If there are no other overloads, it can be ok with just nothing at all. The compiler will produce an error if a type is passed that doesn't provide the necessary operation.
If you really want to limit it to iterators, it's preferable to do so with a static_assert, since it produces an error with a nice custom error message, instead of "ambiguous function call, here are all the gazillion overloads I could find: follows endless list of overloads" or "could not find function, find it yourself".
If there is another templated overload that conflicts, then you do indeed need some enable_if thing. I wrote a blog post about using enable_if with C++11 features, and why default template parameters are not very good for that. I settled with something like this instead:
enum class enabler {};
template <typename Condition>
using EnableIf = typename std::enable_if<Condition::value, enabler>::type;
class MyClass
{
template<class TI, EnableIf<is_iterator<TI>>...> MyClass(TI first, TI last);
};
template<class TI, EnableIf<is_iterator<TI>>...> MyClass::MyClass(TI first, TI last)
{ /* blah */ }
All that you need now is a trait for the test. I think testing for the existence of iterator_category is enough, but it should be done with std::iterator_traits, because pointers are iterators and don't have nested typedefs.
That can be done with the usual techniques that use SFINAE. With C++11, I do the following:
template <typename T>
struct sfinae_true : std::true_type {};
struct is_iterator_tester {
template <typename T>
static sfinae_true<typename std::iterator_traits<T>::iterator_category> test(int);
template <typename>
static std::false_type test(...);
};
template <typename T>
struct is_iterator : decltype(is_iterator_tester::test<T>(0)) {};
All that said, this could have been done with the traditional technique of using a defaulted function parameter:
class MyClass
{
template<class TI>
MyClass(TI first, TI last,
typename std::iterator_traits<T>::iterator_category* = nullptr)
};
template<class TI>
MyClass::MyClass(TI first, TI last,
typename std::iterator_traits<T>::iterator_category*)
{ /* blah */ }