Const and Non Const Template Specialization

Const and non const template specialization

Use this technique:

#include <type_traits>

template< typename T, typename = void >
class Size
{
unsigned int operator() (T) {return sizeof(T);}
};

template< typename T >
class Size< T, typename std::enable_if<
std::is_same< T, char* >::value ||
std::is_same< T, const char* >::value
>::type >
{
unsigned int operator() ( T str ) { /* your code here */ }
};

EDIT: Example of how to define the methods outside of the class definition.

EDIT2: Added helper to avoid repeating the possibly long and complex condition.

EDIT3: Simplified helper.

#include <type_traits>
#include <iostream>

template< typename T >
struct my_condition
: std::enable_if< std::is_same< T, char* >::value ||
std::is_same< T, const char* >::value >
{};

template< typename T, typename = void >
struct Size
{
unsigned int operator() (T);
};

template< typename T >
struct Size< T, typename my_condition< T >::type >
{
unsigned int operator() (T);
};

template< typename T, typename Dummy >
unsigned int Size< T, Dummy >::operator() (T)
{
return 1;
}

template< typename T >
unsigned int Size< T, typename my_condition< T >::type >::operator() (T)
{
return 2;
}

int main()
{
std::cout << Size< int >()(0) << std::endl;
std::cout << Size< char* >()(0) << std::endl;
std::cout << Size< const char* >()(0) << std::endl;
}

which prints

1
2
2

Same template specialization for const & non const type

C++14

I upvoted this answer which is a good showcase for SFINAE. To extend on it, you can simplify the SFINAE expression with std::is_convertible. This is more analogous how overload resolution would work (adding const qualifier).

template<typename T, std::enable_if_t<std::is_convertible_v<T*, const A*>, int> = 0>
void Do(T* data)
{
std::cout << "Do(A*)\n";
}

template<typename T, std::enable_if_t<std::is_convertible_v<T*, const B*>, int> = 0>
void Do(T* data)
{
std::cout << "Do(B*)\n";
}

C++17

As a bonus, in C++17 with constexpr if, you can use a single function for all cases:

template<typename T>
void Do(T data)
{
if constexpr (std::is_convertible_v<T, const A*>)
std::cout << "Do(A*)\n";
else if constexpr (std::is_convertible_v<T, const B*>)
std::cout << "Do(B*)\n";
else
std::cout << "Do() default\n";
}

Specialize template with const and non-const version of another template

You may create a traits for is_B and use it:

template <typename T> struct is_BT : std::false_type {};
template <typename T> struct is_BT<B<T>> : std::true_type {};
template <typename T> struct is_BT<const B<T>> : std::true_type {};

template<typename T>
struct A<T, std::enable_if_t<is_BT<T>::value>>
{
// ...
};

Live Demo

How to specialize a template for const and non const containers?

You'll want the template template parameter to have variadic template argument list (all standard containers in fact have more than one template parameter):

template< template<typename...> class T, typename...Args >
// ^^^

Then this code

#include <vector>
#include <iostream>

template<class T>
struct A{
constexpr static int value=0;
};

template<template<typename...>class T,typename...Args>
struct A<T<Args...>>{//accept non const containers
constexpr static int value=1;
};

template<template<typename...>class T,typename...Args>
struct A<T<Args...> const>{//accept const containers
constexpr static int value=2;
};

int main()
{
std::cout << A<const std::vector<int>>::value;
}

outputs 2 as you expect.

But it still won't work with std::array, because it includes a non-type template parameter in its parameter list (and that can't match a variadic pack of types). This can't be solved in a generic way, I'm afraid.

Function template specialization for both const and non-const gsl::span argument

You may use overload and SFINAE...

template <typename T>
std::enable_if_t<!std::is_floating_point<std::decay_t<T>>::value>
foobar(gsl::span<T> x)
{
std::cout << "generic" << std::endl;
}

template <typename T>
std::enable_if_t<std::is_floating_point<std::decay_t<T>>::value>
foobar(gsl::span<T> x)
{
std::cout << "floating point specialization" << std::endl;
}

C++ template to accept both const and non-const objects

I came up with a solution that works because it can be controlled what type that is used to instantiate the base class:

#include <cstddef>
#include <array>
template <typename T>
class Base
{
public:
explicit Base(T* data) : data_(data) {}

private:
T* data_;
};

template <typename U, std::size_t S>
class A : public Base<U>
{
public:
template<typename T>
explicit A(T (&array)[S]) : Base<T>(array) { ; }

template <typename T, template <typename, std::size_t> class C>
explicit A(C<T, S>& c) : Base<T>(c.begin())
{
;
}

template <typename T, template <typename, std::size_t> class C>
explicit A(C<T, S> const& c) : Base<T const>(c.begin())
{
;
}
};
int main()
{
constexpr std::size_t size{5U};
int c_style_array[size] = {2, 6, 7, 8, 3};
A<int, size> a(c_style_array); // OK
int const c_style_array_const[size] = {2, 6, 7, 8, 3};
A<int const, size> b(c_style_array_const); // OK

std::array<int, size> std_array = {2, 6, 7, 8, 3};
A<int, size> c(std_array); // OK
std::array<int const, size> std_array_const_1 = {2, 6, 7, 8, 3};
A<int const, size> d(std_array_const_1); // OK
std::array<int const, size> const std_array_const_2 = {2, 6, 7, 8, 3};
A<int const, size> e(std_array_const_2); // OK
std::array<int, size> const std_array_const_3 = {2, 6, 7, 8, 3};
A<int const, size> f(std_array_const_3); // OK
}

C++ template to cover const and non-const method

I tend to like simple solutions, so I would go for the free-function approach, possibly adding SFINAE to disable the function for types other than Aggregate:

template <typename Visitor, typename T>
typename std::enable_if< std::is_same<Aggregate,
typename std::remove_const<T>::type
>::value
>::type
visit( Visitor & v, T & s ) { // T can only be Aggregate or Aggregate const
v(s.i);
v(s.d);
}

Where enable_if, is_same and remove_const are actually simple to implement if you don't have a C++0x enabled compiler (or you can borrow them from boost type_traits)

EDIT: While writing the SFINAE approach I realized that there are quite a few problems in providing the plain templated (no SFINAE) solution in the OP, which include the fact that if you need to provide more than one visitable types, the different templates would collide (i.e. they would be as good a match as the others). By providing SFINAE you are actually providing the visit function only for the types that fulfill the condition, transforming the weird SFINAE into an equivalent to:

// pseudocode, [] to mark *optional*
template <typename Visitor>
void visit( Visitor & v, Aggregate [const] & s ) {
v( s.i );
v( s.d );
}

Template const/non-const argument cast

TemplClass<const T> and TemplClass<T> are unrelated types.

For example you may have (partial) specialization to make them really different:

template<typename T>
class TemplClass
{
void generic();
std::string s;
};

template<typename T>
class TemplClass<const T>
{
void foo();
std::vector<int> v;
};

Casting one into the other doesn't make sense.

In the same way

class A
{
char* p;
};

class B
{
char* p;
};

Those 2 classes are unrelated (even if it seems identical).



Related Topics



Leave a reply



Submit