C++ function overloading, alternatives to enable_if? - c++

If I have a function, insert, within a templated class, and I overload it to take either a fill pattern:
void insert(const size_type N, const value_type &element);
or a range pattern:
template <class iterator_type>
void insert(const iterator_type begin, const iterator_type end)
When I specify the value_type of the class to be int, it results in the following call:
insert(500, 50);
being ambiguous, as it assumes 500 to be an int and therefore not a match for size_type N, and instead of calling the fill pattern function, calls the templated range function and fails.
This is a C++03-compatible library,- it can't use external libraries like boost, only builtin C++ code. The only workaround I've found that doesn't require C++11's enable_if would be creating additional overloads, replacing size_type with int, long int, char etc, and calling the fill function from them. Obviously this is problematic because you can have so many different types which match 500. Any suggestions?

Take advantage of SFINAE:
void insert(const size_type N, const value_type &element);
template <class iterator_type>
void insert(iterator_type begin, iterator_type end, char (*)[sizeof(*begin)] = NULL);
The extra dummy argument in the range version of insert will be optimized out by the compiler. Its only role is to eliminate the template function from overloading resolution for types that cannot be used with a dereference operator.
Clarification of the trick:
char (*)[sizeof(*begin)] stands for a pointer to an array of chars whose size is equal to sizeof(*begin). If the variable begin is not dereferenceable then this would be an error. However, in the context of considering a function template during function overload resolution, such an error doesn't stop compilation, but simply discards the template (Substitution Failure Is Not An Error - SFINAE).

Depending on the exact compilers you support, you may find enable_if as a builtin anyway- it's present in TR1.
However, the implementation of enable_if is trivial. You could simply copy it from Boost as well. In fact, it's so short I'm going to post it in this answer.
namespace boost
{
template <bool B, class T = void>
struct enable_if_c {
typedef T type;
};
template <class T>
struct enable_if_c<false, T> {};
template <class Cond, class T = void>
struct enable_if : public enable_if_c<Cond::value, T> {};
template <bool B, class T>
struct lazy_enable_if_c {
typedef typename T::type type;
};
template <class T>
struct lazy_enable_if_c<false, T> {};
template <class Cond, class T>
struct lazy_enable_if : public lazy_enable_if_c<Cond::value, T> {};
template <bool B, class T = void>
struct disable_if_c {
typedef T type;
};
template <class T>
struct disable_if_c<true, T> {};
template <class Cond, class T = void>
struct disable_if : public disable_if_c<Cond::value, T> {};
template <bool B, class T>
struct lazy_disable_if_c {
typedef typename T::type type;
};
template <class T>
struct lazy_disable_if_c<true, T> {};
template <class Cond, class T>
struct lazy_disable_if : public lazy_disable_if_c<Cond::value, T> {};
} // namespace boost

With help from Puppy's answer figured it out:
// Fill insert
void insert(const size_type N, const value_type &element);
// Enable_if template
template <bool condition, class T = void>
struct enable_if_c
{
typedef T type;
};
template <class T>
struct enable_if_c<false, T>
{};
// Range insert:
template <class iterator_type>
iterator insert (const typename enable_if_c<!std::numeric_limits<iterator_type>::is_integer, iterator_type>::type &first, const iterator_type &last);
numeric_limits is C++03-safe and works in all (tested) compilers including MSVC 2010. This checks to make sure the type supplied to range-insert is not an integer.

Related

Avoiding template parameter substitution completely

I have a class that can accept arithmetic types and std::complex. A simplified code of the class is
#include <complex>
template<typename T> struct is_complex : std::false_type {};
template<typename T> struct is_complex<std::complex<T>> : std::true_type {};
template<class T>
struct Foo {
void foo(typename T::value_type t)
requires (is_complex<T>::value) {
}
};
Now, I would like to take the internal type of std::complex and use it as the type of the parameters in the foo function.For example, if T is std::complex<double>, then I want the parameter types to be double.
This function should only be available when T is indeed std::complex.
I thought I could use typename T::value_type as the parameter type, since std::complex has a typedef value_type. Plus, I thought using requires here would avoid T to be substitued in this function in case T wasn't std::complex. Silly me.
The issue is that whenever I create a Foo<FundamentalType> the code breaks, since fundamentals don't have ::value_type.
int main() {
Foo<int> obj; // Breaks the code.
//obj.foo(4); // Function shouldn't be considered in overload resolution ideally...
Foo<std::complex<int>> obj2; // Works
obj2.foo(4); // Works as expected
}
Ideally, I would like the substitution of T to be ignored for this function in case T is not std::complex. Is that possible? If not, how can I circumvent this?
You're on the right track with is_complex: you'd like the same here, but with a different body of the type. For example,
template<typename T> struct complex_value_type {};
template<typename T> struct complex_value_type<std::complex<T>> { using type = T; };
template<typename T>
using complex_value_type_t = typename complex_value_type<T>::type;
Then, at any point, you can call it as complex_value_type_t<T>:
template<class T>
struct Foo {
template<typename T_ = T>
void foo(complex_value_type_t<T_> t)
requires (is_complex<T_>::value) {
}
};
The requires is not absolutely necessary then; it's already covered by complex_value_type_t<T> being defined only for complex<T>.
You just need some type to put in there, until requires can disable the function.
I would do this:
struct nullptr_value_type {using value_type = std::nullptr_t;};
using elem_or_null_t = typename std::conditional_t<is_complex<T>::value, T, nullptr_value_type>::value_type;
void foo(elem_or_null_t t)
requires (is_complex<T>::value)
{}
Use a template class as a template parameter.
#include <complex>
template<template<class> class T> struct is_complex : std::false_type {};
template<> struct is_complex<std::complex> : std::true_type {};
template<template<class> class T>
struct Foo {
void foo(typename T<double>::value_type t)//could be typename<T<TT>> if you made foo a templated function
requires (is_complex<T>::value) {
}
};
int main(){
Foo<std::complex> f;
};
But you will need what to put into it when used. I just hard-coded double but you have to add a new template parameter to Foo or make foo a templated member function

Disable class specialization using concepts

I'm implementing my own version of std::span using Concepts TS. I got stuck implementing these constructors:
template<class Container> constexpr span(Container& cont);
template<class Container> constexpr span(const Container& cont);
Remarks: These constructors shall not participate in overload resolution unless:
Container is not a specialization of span, and
Container is not a specialization of array
How to implement this using concepts?
First, create a trait to check for specializations. array and span look the same in the sense that they take a type parameter and a non-type parameter:
template <typename T, template <typename, auto> class Z>
struct is_specialization : std::false_type { };
template <typename A, auto V, template <typename, auto> class Z>
struct is_specialization<Z<A,V>, Z> : std::true_type { };
template <typename T, template <typename, auto> class Z>
inline constexpr bool is_specialization_v = is_specialization<T, Z>::value;
And then we can build up a concept from that:
// the last bullet point
template <typename T, typename E>
concept ValidForElement =
ConvertibleTo<std::remove_pointer_t<T>(*)[], E(*)[]>;
template <typename T, typename E>
concept AllowedContainer =
// not a specialization of span (note: requires forward declaration of span)
!is_specialization_v<std::remove_cv_t<T>, std::span>
// not a specialization of array
&& !is_specialization_v<std::remove_cv_t<T>, std::array>
// not a raw array
&& !std::is_array_v<std::remove_cv_t<T>>
&& requires (T cont) {
// data(cont) is well-formed and has a valid type
{ data(cont); } -> ValidForElement<E>
// size(cont) is well-formed
{ size(cont); }
};
Which you would use like:
template <typename Element, std::ptrdiff_t Extent = -1>
struct span {
template <typename C> requires AllowedContainer<C, Element>
span(C&);
template <typename C> requires AllowedContainer<C const, Element>
span(C const&);
};
The const-ness requirement there prevents the nice partial-concept-id syntax, but we could just add another concept for that I guess:
template <typename T, typename E>
concept ConstAllowedContainer = AllowedContainer<T const, E>;
template <typename Element, std::ptrdiff_t Extent = -1>
struct span {
template <AllowedContainer<E> C> span(C&);
template <ConstAllowedContainer<E> C> span(C const&);
};
Not sure if there's a cleverer approach here yet.
But really this whole pair-of-constructor thing is probably a mistake and you want to do a forwarding reference:
template <typename Element, std::ptrdiff_t Extent = -1>
struct span {
template <AllowedContainer<E> C>
span(C&&);
};
This last approach requires a few tweaks to the concept (all the remove_cv_t's should become remove_cvref_t's).
You can use type traits to check whether some type is a specialization of span or std::array. This works for me:
#include <type_traits>
template<typename, std::ptrdiff_t> class span;
template <typename T>
struct is_array : std::false_type { };
template <typename T, size_t N>
struct is_array<std::array<T, N>> : std::true_type { };
template <typename T>
struct is_span : std::false_type { };
template <typename T, std::ptrdiff_t P>
struct is_span<span<T, P>> : std::true_type { };
template <typename T>
concept bool NotSpanNotArray = !is_array<T>::value && !is_span<T>::value;
template<typename, std::ptrdiff_t> class span {
public:
template<NotSpanNotArray T> constexpr span(T& cont);
// template<NotSpanNotArray T> constexpr span(const T& cont);
};
Working demo: https://wandbox.org/permlink/M0n60U8Hl4mpacuI
Just I am not 100% sure whether such a solution meets that participate in overload resolution if and only if requirement. Some language-lawyer might clarify this.
UPDATE
I just realized that std::is_array works only for "ordinary" arrays, not std::array. Therefore I added a custom is_array type trait as well.
You are miss-using concepts. In a concept world span constructor will look like this with the concept-ts syntax:
struct span{
span(const ContiguousRange&);
span(ContiguousRange&);
span(const span&) =default;
span(span&) =default;
};
or with c++20:
struct span{
span(const ContiguousRange auto&);
span(ContiguousRange auto&);
span(const span&) =default;
span(span&) =default;
};
Concepts are here to ease abstraction. So that if your interface get more complex when using concepts then you have missed the abstraction. The abstraction here is ContiguousRange (thanks to #Lyberta).

c++ template class; function with arbitrary container type, how to define it?

Okay, simple template question. Say I define my template class something like this:
template<typename T>
class foo {
public:
foo(T const& first, T const& second) : first(first), second(second) {}
template<typename C>
void bar(C& container, T const& baz) {
//...
}
private:
T first;
T second;
}
The question is about my bar function... I need it to be able to use a standard container of some sort, which is why I included the template/typename C part, to define that container type. But apparently that's not the right way to do it, since my test class then complains that:
error: 'bar' was not declared in this scope
So how would I go about implementing my bar function the proper way? That is, as a function of my template class, with an arbitrary container type... the rest of my template class works fine (has other functions that don't result in an error), it's just that one function that's problematic.
EDIT:
Okay, so the specific function (bar) is an eraseInRange function, that erases all elements in a specified range:
void eraseInRange(C& container, T const& firstElement, T const& secondElement) {...}
And an example of how it would be used would be:
eraseInRange(v, 7, 19);
where v is a vector in this case.
EDIT 2:
Silly me! I was supposed to declare the function outside of my class, not in it... pretty frustrating mistake to be making. Anyways, thanks everyone for the help, though the problem was a little different, the information did help me construct the function, since after finding my original problem, I did get some other pleasant errors. So thank you!
Traits solution.
Generalize not more than needed, and not less.
In some cases that solution might not be enough as it will match any template with such signature (e.g. shared_ptr), in which case you could make use of type_traits, very much like duck-typing (templates are duck typed in general).
#include <type_traits>
// Helper to determine whether there's a const_iterator for T.
template<typename T>
struct has_const_iterator
{
private:
template<typename C> static char test(typename C::const_iterator*);
template<typename C> static int test(...);
public:
enum { value = sizeof(test<T>(0)) == sizeof(char) };
};
// bar() is defined for Containers that define const_iterator as well
// as value_type.
template <typename Container>
typename std::enable_if<has_const_iterator<Container>::value,
void>::type
bar(const Container &c, typename Container::value_type const & t)
{
// Note: no extra check needed for value_type, the check comes for
// free in the function signature already.
}
template <typename T>
class DoesNotHaveConstIterator {};
#include <vector>
int main () {
std::vector<float> c;
bar (c, 1.2f);
DoesNotHaveConstIterator<float> b;
bar (b, 1.2f); // correctly fails to compile
}
A good template usually does not artificially restrict the kind of types for which they are valid (why should they?). But imagine in the example above you need to have access to an objects const_iterator, then you can use SFINAE and type_traits to put those constraints on your function.
Or just to as the standard library does
Generalize not more than needed, and not less.
template <typename Iter>
void bar (Iter it, Iter end) {
for (; it!=end; ++it) { /*...*/ }
}
#include <vector>
int main () {
std::vector<float> c;
bar (c.begin(), c.end());
}
For more such examples, look into <algorithm>.
This approach's strength is its simplicity and is based on concepts like ForwardIterator. It will even work for arrays. If you want to report errors right in the signature, you can combine it with traits.
std containers with signature like std::vector (not recommended)
The simplest solution is approximated by Kerrek SB already, though it is invalid C++. The corrected variant goes like so:
#include <memory> // for std::allocator
template <template <typename, typename> class Container,
typename Value,
typename Allocator=std::allocator<Value> >
void bar(const Container<Value, Allocator> & c, const Value & t)
{
//
}
However: this will only work for containers that have exactly two template type arguments, so will fail miserably for std::map (thanks Luc Danton).
Any kind of secondary template arguments (not recommended)
The corrected version for any secondary parameter count is as follows:
#include <memory> // for std::allocator<>
template <template <typename, typename...> class Container,
typename Value,
typename... AddParams >
void bar(const Container<Value, AddParams...> & c, const Value & t)
{
//
}
template <typename T>
class OneParameterVector {};
#include <vector>
int main () {
OneParameterVector<float> b;
bar (b, 1.2f);
std::vector<float> c;
bar (c, 1.2f);
}
However: this will still fail for non-template containers (thanks Luc Danton).
Make the template templated on a template template parameter:
template <template <typename, typename...> class Container>
void bar(const Container<T> & c, const T & t)
{
//
}
If you don't have C++11, then you can't use variadic templates, and you have to provide as many template parameters as your container takes. For example, for a sequence container you might need two:
template <template <typename, typename> class Container, typename Alloc>
void bar(const Container<T, Alloc> & c, const T & t);
Or, if you only want to allow allocators which are themselves template instances:
template <template <typename, typename> class Container, template <typename> class Alloc>
void bar(const Container<T, Alloc<T> > & c, const T & t);
As I suggested in the comments, I would personally prefer to make the entire container a templated type and use traits to check if it's valid. Something like this:
template <typename Container>
typename std::enable_if<std::is_same<typename Container::value_type, T>::value, void>::type
bar(const Container & c, const T & t);
This is more flexible since the container can now be anything that exposes the value_type member type. More sophisticated traits for checking for member functions and iterators can be conceived of; for example, the pretty printer implements a few of those.
C++20 solution with Concepts and Ranges
In C++20, with the addition of Concepts and Ranges library, we can solve this simply with std::ranges::common_range:
void printContainer(const std::ranges::common_range auto & container);
{
for(const auto& item : container) std::cout << item;
}
Here, common_range is a concept that all stl containers satisfy. And you can get container's value type with:
std::ranges::range_value_t<decltype(container)>
You can also create your own container type that satisfy the concept with well defined iterator type and it begin() and it end() functions.
Alternatively, you can also use std::ranges::range, which has a bit less strict requirement than common_range, so it could allow more custom types.
Attempting to call the function with a non-satisfying type would give you error like template argument deduction/substitution failed: constraints not satisfied.
Here's the latest and expanded version of this answer and significant improvement over answer by Sabastian.
The idea is to define all traits of STL containers. Unfortunately, this gets tricky very fast and fortunately lot of people have worked on tuning this code. These traits are reusable so just copy and past below code in file called type_utils.hpp (feel free to change these names):
//put this in type_utils.hpp
#ifndef commn_utils_type_utils_hpp
#define commn_utils_type_utils_hpp
#include <type_traits>
#include <valarray>
namespace common_utils { namespace type_utils {
//from: https://raw.githubusercontent.com/louisdx/cxx-prettyprint/master/prettyprint.hpp
//also see https://gist.github.com/louisdx/1076849
namespace detail
{
// SFINAE type trait to detect whether T::const_iterator exists.
struct sfinae_base
{
using yes = char;
using no = yes[2];
};
template <typename T>
struct has_const_iterator : private sfinae_base
{
private:
template <typename C> static yes & test(typename C::const_iterator*);
template <typename C> static no & test(...);
public:
static const bool value = sizeof(test<T>(nullptr)) == sizeof(yes);
using type = T;
void dummy(); //for GCC to supress -Wctor-dtor-privacy
};
template <typename T>
struct has_begin_end : private sfinae_base
{
private:
template <typename C>
static yes & f(typename std::enable_if<
std::is_same<decltype(static_cast<typename C::const_iterator(C::*)() const>(&C::begin)),
typename C::const_iterator(C::*)() const>::value>::type *);
template <typename C> static no & f(...);
template <typename C>
static yes & g(typename std::enable_if<
std::is_same<decltype(static_cast<typename C::const_iterator(C::*)() const>(&C::end)),
typename C::const_iterator(C::*)() const>::value, void>::type*);
template <typename C> static no & g(...);
public:
static bool const beg_value = sizeof(f<T>(nullptr)) == sizeof(yes);
static bool const end_value = sizeof(g<T>(nullptr)) == sizeof(yes);
void dummy(); //for GCC to supress -Wctor-dtor-privacy
};
} // namespace detail
// Basic is_container template; specialize to derive from std::true_type for all desired container types
template <typename T>
struct is_container : public std::integral_constant<bool,
detail::has_const_iterator<T>::value &&
detail::has_begin_end<T>::beg_value &&
detail::has_begin_end<T>::end_value> { };
template <typename T, std::size_t N>
struct is_container<T[N]> : std::true_type { };
template <std::size_t N>
struct is_container<char[N]> : std::false_type { };
template <typename T>
struct is_container<std::valarray<T>> : std::true_type { };
template <typename T1, typename T2>
struct is_container<std::pair<T1, T2>> : std::true_type { };
template <typename ...Args>
struct is_container<std::tuple<Args...>> : std::true_type { };
}} //namespace
#endif
Now you can use these traits to make sure our code only accepts container types. For example, you can implement append function that appends one vector to another like this:
#include "type_utils.hpp"
template<typename Container>
static typename std::enable_if<type_utils::is_container<Container>::value, void>::type
append(Container& to, const Container& from)
{
using std::begin;
using std::end;
to.insert(end(to), begin(from), end(from));
}
Notice that I'm using begin() and end() from std namespace just to be sure we have iterator behavior. For more explanation see my blog post.

Basic Typelist functionality

I'm having a bit of trouble getting my head around TypeLists or lists of templates. That is:
class nulltype{};
template <typename HEAD, typename TAIL>
struct tlist
{
typedef HEAD head;
typedef TAIL tail;
};
template <class TList>
class OutputClass
{
public:
void output(Type t)
{
std::cout << t << endl;
}
};
typedef tlist<double,tlist<float,NullType> > floatingPoints;
typedef tlist<std::string,tlist<char *,NullType> > text;
int main()
{
OutputClass<floatingPoints> floats = OutputClass<floatingPoints>();
floats.output(1.0f);
OutputClass<text> strings = OutputClass<text>();
floats.output("hello");
return 0;
}
Basically my goal is that I would like OutputClass.output to output the parameter passed to it, but only if that class instances typelist contains the type passed into the function. ie. referring to the above code: floats can only output float type and double type as defined by its typelist, "floatingPoints". Should a string or int get passed in, I would hope it would error.
I'm having one hell of a time trying to find any examples on how to do this, I've found the indexing examples and length examples a million times over, and they have helped me a lot, but i just can't seem to figure this last bit out. Any and all help will be appreciated.
We first need some helper templates. The first one checks if two types are the same:
template <typename T, typename U>
struct is_same
{
// Default case: T and U are not the same type
static const bool value = false;
};
template <typename T>
struct is_same<T, T>
{
// Specialization: both template arguments are of the same type
static const bool value = true;
};
We can now use the boolean is_same<T, U>::value to determine if the types T and U are equivalent.
Using is_same we can now write a template which checks if a specific type occurs in a type list:
template <typename TList, typename T>
struct contains
{
static const bool value =
is_same<typename TList::head, T>::value // Base case
|| contains<typename TList::tail, T>::value; // Recursion
};
template <typename T>
struct contains<nulltype, T>
{
// Termination condition
static const bool value = false;
};
This is a recursive template, using a specialization on nulltype to terminate the recursion.
One final helper template we need is enable_if:
template <bool Cond, typename T=void>
struct enable_if
{
// Default case: Cond assumed to be false, no typedef
};
template <typename T>
struct enable_if<true, T>
{
// Specialization: Cond is true, so typedef
typedef T type;
};
enable_if<true, T>::type yields T, whereas enable_if<false, T>::type is undefined. We exploit the SFINAE rule the enable or disable a function depending on its template argument, like so:
template <typename TList>
class OutputClass
{
public:
// Only enable the function if T is in TList (by SFINAE)
template <typename T>
typename enable_if<contains<TList, T>::value>::type
output(T t)
{
std::cout << t << std::endl;
}
};
Quite a trip, but if you understand all of this you're well on your way to master template metaprogramming. For an in-depth discussion of template metaprogramming I recommend you pick up a copy of C++ Template Metaprogramming (ISBN-13: 9780321227256).

Can C++'s value_type be extended from iterator_traits to all types?

I would like to create a construct similar to std::iterator_traits::value_type that can work seamlessly for all types using the same syntax. Imagine we have the following:
template <typename T>
struct value_type {
typedef T type;
};
#define VALUE_TYPE(T) typename value_type<T >::type
This will work for POD types. I can specialize it for my own class:
struct MyClass {
typedef float value_type;
};
template <>
struct value_type<MyClass> {
typedef MyClass::value_type type;
};
though I would prefer to avoid extra value_type instantiations in an ideal world.
The problem is with STL iterators. I need a specialization that gets me to the iterator hierarchy. This fails because the compiler chooses the base case:
template <>
struct value_type<std::_Iterator_base_aux> { // MSVC implementation
typedef value_type type;
};
Choosing a class higher up the hierarchy (_Iterator_with_base would be most natural because that is where value_type is defined) fails because it requires specifying all the iterator traits as template arguments.
Is what I'm trying to do even possible in C++?
You can use SFINAE to detect the presence of the value_type typedef. No need to specialize for individual types (which might not be possible, since you'd be relying entirely on internal implementation details).
#include <vector>
template <class T>
struct has_value_type
{
typedef char true_type;
typedef char false_type[2];
//template not available if there's no nested value_type in U's scope
template <class U>
static true_type test(typename U::value_type* );
//fallback
template <class U>
static false_type& test(...);
//tests which overload of test is chosen for T
static const bool value = sizeof(test<T>(0)) == sizeof(true_type);
};
template <class T, bool b>
struct value_type_impl;
template <class T>
struct value_type_impl<T, false> //if T doesn't define value_type
{
typedef T type;
};
template <class T>
struct value_type_impl<T, true> //if T defines value_type
{
typedef typename T::value_type type;
};
template <class T>
struct value_type: value_type_impl<T, has_value_type<T>::value>
{
};
struct MyClass {
typedef float value_type;
};
template <class T>
int foo(T )
{
return typename value_type<T>::type();
}
int main()
{
foo(MyClass());
std::vector<int> vec;
foo(vec.begin());
foo(10);
}
UncleBens has used SFINAE, but there is actually simpler:
template <class T>
struct value_type
{
typedef typename T::value_type type;
};
Now, if you want to use it with a class you control, the easiest way is:
struct MyClass { typedef float value_type; };
BOOST_MPL_ASSERT((boost::is_same< MyClass::value_type,
typename value_type<MyClass>::type >));
And if you want to use for a class you do not control, you still have specialization:
struct ThirdPartyClass {};
template <>
struct value_type<ThirdPartyClass> { typedef int type; }
If you try to use value_type for a class that does not have an inner typedef and for which no specialization is available, it's a compilation error (and a message you are not likely to understand at first glance...)