Recursively dereference pointer - c++

While trying to answer one question here, I found this question:
How to recursively dereference pointer (C++03)?
Adapted code from the answer is following:
template<typename T> T& dereference(T &v) { return v; }
template<typename T> const T& dereference(const T &v) { return v; }
template <typename T>
typename std::enable_if<!std::is_pointer<T>::value, T&>::type
dereference(T *v) {
return dereference(*v);
}
However, in this test it is failing to dereference pointer-to-pointer into the value type:
template <typename T>
class A
{
public:
bool compare(T a, T b){
return dereference(a) < dereference(b);
}
};
int main()
{
int u = 10;
int *v = &u;
int **w = &v;
int i = 5;
int *j = &i;
int **k = &j;
A<int> a;
A<int*> b;
A<int**> c;
std::cout << a.compare(i, u) << std::endl;
std::cout << b.compare(j, v) << std::endl;
// This fails - 5 < 10 == 0
std::cout << **k << " < " << **w << " == " << c.compare(k, w) << std::endl;
return 0;
}
Obviously, w and k are derefenced only one time, which causes operator< to be called on two pointers.
I can fix this by adding the following:
template <typename T>
typename std::enable_if<!std::is_pointer<T>::value, T&>::type
dereference(T **v) {
return dereference(*v);
}
but then it will fail for int***.
Is there any way to make this recursively without adding levels manually?
Note This is just "theoretical" question.

This is possible with the use of a custom can_dereference trait:
template <typename T>
struct can_dereference_helper {
template <typename U, typename = decltype(*std::declval<U>())>
static std::true_type test(U);
template <typename...U>
static std::false_type test(U...);
using type = decltype(test(std::declval<T>()));
};
template <typename T>
struct can_dereference :
can_dereference_helper<typename std::decay<T>::type>::type {};
and some mutually-recursive functions with a bit'o tag dispatching:
template <typename T>
auto recursive_dereference(T&& t, std::false_type) ->
decltype(std::forward<T>(t)) {
return std::forward<T>(t);
}
template <typename T>
auto recursive_dereference(T&& t) ->
decltype(recursive_dereference(std::forward<T>(t), can_dereference<T>{}));
template <typename T>
auto recursive_dereference(T&& t, std::true_type) ->
decltype(recursive_dereference(*std::forward<T>(t))) {
return recursive_dereference(*std::forward<T>(t));
}
template <typename T>
auto recursive_dereference(T&& t) ->
decltype(recursive_dereference(std::forward<T>(t), can_dereference<T>{})) {
return recursive_dereference(std::forward<T>(t), can_dereference<T>{});
}
See it work live at Coliru. This may seem like overkill compared to Kerrek's answer, but I went for a generic approach that will dereference anything that supports operator*. I'll let you decide which tool fits your problem best.

You can do it with a trait to compute the ultimate return type, here dubbed remove_all_pointers:
#include <type_traits>
template <typename T> struct remove_all_pointers
{ typedef typename std::remove_reference<T>::type type; };
template <typename T> struct remove_all_pointers<T *>
{ typedef typename std::remove_reference<T>::type type; };
template <typename T>
T & dereference(T & p)
{ return p; }
template <typename U>
typename remove_all_pointers<U>::type & dereference(U * p)
{ return dereference(*p); }
int main(int argc, char * argv[])
{
return dereference(argv);
}
You may need to add CV-variants; I'm still thinking about that.

Related

aggregate-initializable tuple like data structure

I want to store multiple non-movable types in a single variable.
At the very first, I have tried std::tuple at the very first, but it fails.
#include <tuple>
template<typename T>
struct No {
No(T){}
No(const No &) = delete;
No(No &&) = delete;
};
struct Handmade {
No<int> a;
No<double> b;
No<char> c;
};
template<typename T>
auto no() -> No<T> { return No<T>(T()); }
auto main() -> int
{
Handmade h = {no<int>(), no<double>(), no<char>()}; // good
auto tuple = std::make_tuple(no<int>(), no<double>(), no<char>()); // fails
return 0;
}
Here, Handmade type can be initialized through aggregate initialization.
However, std::tuple is not aggregate-initialzable, it doesn't work.
Since it should be variadic, I cannot write such type Handmade for my purpose.
Is it possible to implement such variadic tepmlate data structure in current C++ standard or is there any workaround?
Yes, you can write your own aggregate tuple like this:
template <int I, typename T>
struct MyTupleElem
{
T value{};
template <int J> requires(I == J) T &get() {return value;}
template <int J> requires(I == J) const T &get() const {return value;}
};
template <typename T, typename ...P>
struct MyTupleHelper;
template <int ...I, typename ...P>
struct MyTupleHelper<std::integer_sequence<int, I...>, P...>
: MyTupleElem<I, P>...
{
using MyTupleElem<I, P>::get...;
};
template <typename ...P>
struct MyTuple : MyTupleHelper<std::make_integer_sequence<int, sizeof...(P)>, P...> {};
template <typename ...P>
MyTuple(P &&...) -> MyTuple<std::decay_t<P>...>;
template <typename T>
struct No
{
T value;
No(T value) : value(value) {}
No(const No &) = delete;
No(No &&) = delete;
};
template <typename T>
No<T> no(T t) {return No<T>(t);}
int main()
{
MyTuple h = {no<int>(1), no<double>(2.3), no<char>('4')};
std::cout << h.get<0>().value << '\n';
std::cout << h.get<1>().value << '\n';
std::cout << h.get<2>().value << '\n';
}
And I think it's a good way to make tuples in general, even if you don't want aggregate-ness. Last time I tested, such tuples could tolerate more elements than the classic ones with the bases chained on top of each other.

C++ Safe Recursive Dereference

I would like an elegant way to safely read data in a field which is wrapped in "nullable types" such as std::optional and std::shared_ptr. Take as example:
#include <iostream>
#include <memory>
#include <optional>
struct Entry
{
std::optional<std::string> name;
};
struct Container
{
std::optional<std::shared_ptr<Entry>> entry;
};
int main()
{
Entry entry{"name"};
Container container{std::make_shared<Entry>(entry)};
// ...
return 0;
}
To read the "name" field from Entry given a Container, I could write:
std::cout << *((*container.entry)->name) << std::endl;
But I don't find this particularly easy to read or write. And since the optionals and shared pointers may not be set, I can't anyway.
I want to avoid code like this:
if (container.entry)
{
const auto ptr = *container.entry;
if (ptr != nullptr)
{
const auto opt = ptr->name;
if (opt)
{
const std::string name = *opt;
std::cout << name << std::endl;
}
}
}
And I am looking for something more like this:
const auto entry = recursive_dereference(container.entry);
const auto name = recursive_dereference(entry.name);
std::cout << name.value_or("empty") << std::endl;
This would be based on this recursive_dereference implementation.
The trouble is, it would crash if an optional or shared_ptr is not set. Is there a way to modify recursive_dereference so that it returns its result in an optional which is left empty when a field along the way is unset?
I think we could use std::enable_if_t<std::is_constructible<bool, T>::value to check if the field can be used as a bool in an if (which would be the case for optionals and shared pointers) which would allow us to check if they are set. If they are set we can continue the dereferencing recursion. If one is not set, we can interrupt the recursion and return an empty optional of the final type.
Unfortunately, I couldn't formulate this into working code. The solution should at best be limited to "C++14 with optionals".
Update:
First a remark. I realized that using std::is_constructible<bool, T> is unnecessary. recursive_dereference checks if a type can be dereferenced and when it can be then we can check if it is set with if (value). At least it would work with optionals and shared pointers.
An alternative I found is first separately checking if it is safe to dereference the value and then call recursive_dereference unmodified.
So we can do:
if (is_safe(container.entry)) {
const auto entry = recursive_dereference(container.entry);
// use entry
}
Implementation of is_safe:
template<typename T>
bool is_safe(T&& /*t*/, std::false_type /*can_deref*/)
{
return true;
}
// Forward declaration
template<typename T>
bool is_safe(T&& t);
template<typename T>
bool is_safe(T&& t, std::true_type /*can_deref*/)
{
if (t)
{
return is_safe(*std::forward<T>(t));
}
return false;
}
template<typename T>
bool is_safe(T&& t)
{
return is_safe(std::forward<T>(t), can_dereference<T>{});
}
I'm still open for a better solution that would avoid checking and deferencing separately. So that we get a value or "empty" in one pass.
Update 2
I managed to get a version that does not need a separate check. We have to explicitly give the final type that we expect as template parameter though. It returns an optional with the value or an empty optional if one reference along the way is not set.
template <typename FT, typename T>
auto deref(T&& t, std::false_type) -> std::optional<FT>
{
return std::forward<T>(t);
}
template <typename FT, typename T>
auto deref(T&& t) -> std::optional<FT>;
template <typename FT, typename T>
auto deref(T&& t, std::true_type) -> std::optional<FT>
{
if (t)
{
return deref<FT>(*std::forward<T>(t));
}
return std::nullopt;
}
template <typename FT, typename T>
auto deref(T&& t) -> std::optional<FT>
{
return deref<FT>(std::forward<T>(t), can_dereference<T>{});
}
Usage:
std::cout << deref<Entry>(container.entry).has_value() << std::endl;
std::cout << deref<Entry>(emptyContainer.entry).has_value() << std::endl;
Output:
1
0
There are two solutions I can recommend you:
if_valid(value, thenLambda, elseLambda) construct:
#include <iostream>
#include <memory>
#include <optional>
struct Entry
{
std::optional<std::string> name;
};
struct Container
{
std::optional<std::shared_ptr<Entry>> entry;
};
template<typename V, typename Then, typename Else>
auto if_valid(const V& v, Then then, Else els)
{
return then(v);
}
template<typename V, typename Then, typename Else>
auto if_valid(const std::optional<V>& iv, Then then, Else els)
{
if (iv) {
return if_valid(*iv, std::move(then), std::move(els));
} else {
return els();
}
}
template<typename V, typename Then, typename Else>
auto if_valid(const std::shared_ptr<V>& iv, Then then, Else els)
{
if (iv) {
return if_valid(*iv, std::move(then), std::move(els));
} else {
return els();
}
}
int main()
{
Entry entry{"name"};
Container container{std::make_shared<Entry>(entry)};
std::cout
<< if_valid(
container.entry,
/* then */ [&](auto&& entry1) { return entry1.name; },
/* else */ [] () { return std::optional<std::string>(); }
).value_or("empty") << std::endl;
return 0;
}
A generic resolver with then and else path: (this has the benefit that you might simply have .name as a resolver as well as operator*)
#include <iostream>
#include <memory>
#include <optional>
#include <tuple>
#include <type_traits>
struct Entry
{
std::optional<std::string> name;
};
struct Container
{
std::optional<std::shared_ptr<Entry>> entry;
};
struct resolve_shared_ptr
{
template<typename T, typename Then, typename Else>
auto operator()(const std::shared_ptr<T>& t, Then then, Else els) const
{
if (t) {
then(*t);
} else {
els();
}
}
};
struct resolve_optional
{
template<typename T, typename Then, typename Else>
auto operator()(const std::optional<T>& t, Then then, Else els) const
{
if (t) {
then(*t);
} else {
els();
}
};
};
static_assert(std::is_invocable_v<
resolve_optional,
const std::optional<std::string>&,
decltype([](const auto&) {}),
decltype([]() {})
>);
template<typename T, typename Then, typename Else, size_t r, typename... Resolvers>
void resolve_r(const T& t, Then then, Else els, std::integral_constant<size_t, r>, const std::tuple<Resolvers...>& resolvers)
{
if constexpr(r < sizeof...(Resolvers)) {
if constexpr (std::is_invocable_v<decltype(std::get<r>(resolvers)), const T&, decltype([](auto&&) {}), Else>) {
std::get<r>(resolvers)(
t,
/* then */ [&](const auto& next_t) { resolve(next_t, then, els, resolvers); },
els
);
} else {
resolve_r(t, then, els, std::integral_constant<size_t, r + 1>(), resolvers);
//return resolve_r(t, then, els, r + 1, resolvers);
}
} else {
then(t);
}
}
template<typename T, typename Then, typename Else, typename... Resolvers>
void resolve(const T& t, Then then, Else els, const std::tuple<Resolvers...>& resolvers)
{
resolve_r(t, then, els, std::integral_constant<size_t, 0>(), resolvers);
}
int main()
{
Entry entry{"name"};
Container container{std::make_shared<Entry>(entry)};
resolve(
container.entry,
/* then */ [](const auto& res) { std::cout << res; },
/* else */ []() { std::cout << "empty"; },
std::make_tuple(
resolve_optional(),
resolve_shared_ptr(),
[](const Entry& entry1, auto then, auto els) { then(entry1.name); }
)
);
std::cout << std::endl;
return 0;
}
Combining recursive_dereference and convert_optional_fact, I end-up with this:
#include <functional>
#include <iostream>
#include <memory>
#include <optional>
#include <string>
#include <type_traits>
// can_dereference
template <typename T>
struct can_dereference_helper
{
template <typename U, typename = decltype(*std::declval<U>())>
static std::true_type test(U);
template <typename...U>
static std::false_type test(U...);
using type = decltype(test(std::declval<T>()));
};
template <typename T>
struct can_dereference : can_dereference_helper<typename std::decay<T>::type>::type {};
// deref
template <typename FT, typename T>
auto deref(T&& t, std::false_type) -> std::optional<FT>
{
return std::forward<T>(t);
}
template <typename FT, typename T>
auto deref(T&& t) -> std::optional<FT>;
template <typename FT, typename T>
auto deref(T&& t, std::true_type) -> std::optional<FT>
{
if (t)
{
return deref<FT>(*std::forward<T>(t));
}
return std::nullopt;
}
template <typename FT, typename T>
auto deref(T&& t) -> std::optional<FT>
{
return deref<FT>(std::forward<T>(t), can_dereference<T>{});
}
// get_field
template <typename> struct is_optional : std::false_type {};
template <typename T> struct is_optional<std::optional<T>> : std::true_type {};
template <typename O, typename F>
auto convert_optional(O&& o, F&& f)
-> std::enable_if_t<
is_optional<std::decay_t<O>>::value,
std::optional<std::decay_t<decltype(std::invoke(std::forward<F>(f),
*std::forward<O>(o)))>>>
{
if (o)
{
return std::invoke(std::forward<F>(f), *o);
}
return std::nullopt;
}
template <typename O, typename F>
auto get_field(O&& o, F&& f)
-> decltype(convert_optional(std::forward<O>(o),
std::forward<F>(f)).value_or(std::nullopt))
{
return convert_optional(std::forward<O>(o),
std::forward<F>(f)).value_or(std::nullopt);
}
// Test data
struct Entry
{
std::optional<std::string> name;
};
struct Container
{
std::optional<std::shared_ptr<Entry>> entry;
};
// main
int main()
{
Container emptyContainer{};
Entry entry{"name"};
Container container{std::make_shared<Entry>(entry)};
std::cout << deref<Entry>(container.entry).has_value() << std::endl;
std::cout << deref<Entry>(emptyContainer.entry).has_value() << std::endl;
const auto name = get_field(deref<Entry>(container.entry), &Entry::name);
std::cout << name.value_or("empty") << std::endl;
const auto emptyName = get_field(deref<Entry>(emptyContainer.entry), &Entry::name);
std::cout << emptyName.value_or("empty") << std::endl;
return 0;
}
Output:
1
0
name
empty
Play with it in Online GDB.
With this, we can get from the container to the field in one line:
get_field(deref<Entry>(container.entry), &Entry::name)
We get an optional with the string for "name" or and empty optional if something is not set.
Still open:
std::invoke is C++17 and I need C++14 (except for std::optional which is allowed)
It would be nice if we could deduce the final type automatically in deref so that we don't have to specify Entry in deref<Entry> in the line above.

In C++, how to return different generic types depending on argument in a class?

I have this code:
template<class T1, class T2>
class Pair
{
private:
T1 first;
T2 second;
public:
void SetFirst(T1 first)
{
this.first = first;
}
void SetSecond(T2 second)
{
this.second = second;
}
T1 GetFirst()
{
return first;
}
T2 GetSecond()
{
return second;
}
};
How could I implement two single methods SetValue() and GetValue(), instead of the four I have, that decides depending on parameters which generic type that should be used? For instance I'm thinking the GetValue() method could take an int parameter of either 1 or 2 and depending on the number, return either a variable of type T1 or T2. But I don't know the return type beforehand so is there anyway to solve this?
Not sure to understand what do you want and not exactly what you asked but...
I propose the use of a wrapper base class defined as follows
template <typename T>
class wrap
{
private:
T elem;
public:
void set (T const & t)
{ elem = t; }
T get () const
{ return elem; }
};
Now your class can be defined as
template <typename T1, typename T2>
struct Pair : wrap<T1>, wrap<T2>
{
template <typename T>
void set (T const & t)
{ wrap<T>::set(t); }
template <typename T>
T get () const
{ return wrap<T>::get(); }
};
or, if you can use C++11 and variadic templates and if you define a type traits getType to get the Nth type of a list,
template <std::size_t I, typename, typename ... Ts>
struct getType
{ using type = typename getType<I-1U, Ts...>::type; };
template <typename T, typename ... Ts>
struct getType<0U, T, Ts...>
{ using type = T; };
you can define Pair in a more flexible way as follows
template <typename ... Ts>
struct Pair : wrap<Ts>...
{
template <typename T>
void set (T const & t)
{ wrap<T>::set(t); }
template <std::size_t N, typename T>
void set (T const & t)
{ wrap<typename getType<N, Ts...>::type>::set(t); }
template <typename T>
T get () const
{ return wrap<T>::get(); }
template <std::size_t N>
typename getType<N, Ts...>::type get ()
{ return wrap<typename getType<N, Ts...>::type>::get(); }
};
Now the argument of set() can select the correct base class and the correct base element
Pair<int, long> p;
p.set(0); // set the int elem
p.set(1L); // set the long elem
otherwise, via index, you can write
p.set<0U>(3); // set the 1st (int) elem
p.set<1U>(4); // set the 2nd (long) elem
Unfortunately, the get() doesn't receive an argument, so the type have to be explicited (via type or via index)
p.get<int>(); // get the int elem value
p.get<long>(); // get the long elem value
p.get<0U>(); // get the 1st (int) elem value
p.get<1U>(); // get the 2nd (long) elem value
Obviously, this didn't work when T1 is equal to T2
The following is a (C++11) full working example
#include <iostream>
template <std::size_t I, typename, typename ... Ts>
struct getType
{ using type = typename getType<I-1U, Ts...>::type; };
template <typename T, typename ... Ts>
struct getType<0U, T, Ts...>
{ using type = T; };
template <typename T>
class wrap
{
private:
T elem;
public:
void set (T const & t)
{ elem = t; }
T get () const
{ return elem; }
};
template <typename ... Ts>
struct Pair : wrap<Ts>...
{
template <typename T>
void set (T const & t)
{ wrap<T>::set(t); }
template <std::size_t N, typename T>
void set (T const & t)
{ wrap<typename getType<N, Ts...>::type>::set(t); }
template <typename T>
T get () const
{ return wrap<T>::get(); }
template <std::size_t N>
typename getType<N, Ts...>::type get ()
{ return wrap<typename getType<N, Ts...>::type>::get(); }
};
int main()
{
//Pair<int, int> p; compilation error
Pair<int, long, long long> p;
p.set(0);
p.set(1L);
p.set(2LL);
std::cout << p.get<int>() << std::endl; // print 0
std::cout << p.get<long>() << std::endl; // print 1
std::cout << p.get<long long>() << std::endl; // print 2
p.set<0U>(3);
p.set<1U>(4);
p.set<2U>(5);
std::cout << p.get<0U>() << std::endl; // print 3
std::cout << p.get<1U>() << std::endl; // print 4
std::cout << p.get<2U>() << std::endl; // print 5
}
C++ is statically typed, so the argument given must be a template-argument instead a function-argument.
And while it will look like just one function each to the user, it's really two.
template <int i = 1> auto GetValue() -> std::enable_if_t<i == 1, T1> { return first; }
template <int i = 2> auto GetValue() -> std::enable_if_t<i == 2, T2> { return second; }
template <int i = 1> auto SetValue(T1 x) -> std::enable_if_t<i == 1> { first = x; }
template <int i = 2> auto SetValue(T2 x) -> std::enable_if_t<i == 2> { second = x; }
I use SFINAE on the return-type to remove the function from consideration unless the template-argument is right.
For this particular situation, you should definitely prefer std::pair or std::tuple.
You can simply overload SetValue() (provided T1 and T2 can be distinguished, if not you have a compile error):
void SetValue(T1 x)
{ first=x; }
void SetValue(T2 x)
{ second=x; }
Then, the compiler with find the best match for any call, i.e.
Pair<int,double> p;
p.SetValue(0); // sets p.first
p.SetValue(0.0); // sets p.second
With GetValue(), the information of which element you want to retrieve cannot be inferred from something like p.GetValue(), so you must provide it somehow. There are several options, such as
template<typename T>
std::enable_if_t<std::is_same<T,T1>,T>
GetValue() const
{ return first; }
template<typename T>
std::enable_if_t<std::is_same<T,T2>,T>
GetValue() const
{ return second; }
to be used like
auto a = p.GetValue<int>();
auto b = p.GetValue<double>();
but your initial version is good enough.

Unify C++ templates for pointers, values and smart pointers

My real example is quite big, so I will use a simplified one. Suppose I have a data-type for a rectangle:
struct Rectangle {
int width;
int height;
int computeArea() {
return width * height;
}
}
And another type that consumes that type, for example:
struct TwoRectangles {
Rectangle a;
Rectangle b;
int computeArea() {
// Ignore case where they overlap for the sake of argument!
return a.computeArea() + b.computeArea();
}
};
Now, I don't want to put ownership constraints on users of TwoRectangles, so I would like to make it a template:
template<typename T>
struct TwoRectangles {
T a;
T b;
int computeArea() {
// Ignore case where they overlap for the sake of argument!
return a.computeArea() + b.computeArea();
}
};
Usages:
TwoRectangles<Rectangle> x;
TwoRectangles<Rectangle*> y;
TwoRectangles<std::shared_ptr<Rectangle>> z;
// etc...
The problem is that if the caller wants to use pointers, the body of the function should be different:
template<typename T>
struct TwoRectangles {
T a;
T b;
int computeArea() {
assert(a && b);
return a->computeArea() + b->computeArea();
}
};
What is the best way of unifying my templated function so that the maxiumum amount of code is reused for pointers, values and smart pointers?
One way of doing this, encapsulating everything within TwoRectangles, would be something like:
template<typename T>
struct TwoRectangles {
T a;
T b;
int computeArea() {
return areaOf(a) + areaOf(b);
}
private:
template <class U>
auto areaOf(U& v) -> decltype(v->computeArea()) {
return v->computeArea();
}
template <class U>
auto areaOf(U& v) -> decltype(v.computeArea()) {
return v.computeArea();
}
};
It's unlikely you'll have a type for which both of those expressions are valid. But you can always add additional disambiguation with a second argument to areaOf().
Another way, would be to take advantage of the fact that there already is a way in the standard library of invoking a function on whatever: std::invoke(). You just need to know the underlying type:
template <class T, class = void>
struct element_type {
using type = T;
};
template <class T>
struct element_type<T, void_t<typename std::pointer_traits<T>::element_type>> {
using type = typename std::pointer_traits<T>::element_type;
};
template <class T>
using element_type_t = typename element_type<T>::type;
and
template<typename T>
struct TwoRectangles {
T a;
T b;
int computeArea() {
using U = element_type_t<T>;
return std::invoke(&U::computeArea, a) +
std::invoke(&U::computeArea, b);
}
};
I actually had a similar problem some time ago, eventually i opted not to do it for now (because it's a big change), but it spawned a solution that seems to be correct.
I thought about making a helper function to access underlying value if there is any indirection. In code it would look like this, also with an example similar to yours.
#include <iostream>
#include <string>
#include <memory>
namespace detail
{
//for some reason the call for int* is ambiguous in newer standard (C++14?) when the function takes no parameters. That's a dirty workaround but it works...
template <class T, class SFINAE = decltype(*std::declval<T>())>
constexpr bool is_indirection(bool)
{
return true;
}
template <class T>
constexpr bool is_indirection(...)
{
return false;
}
}
template <class T>
constexpr bool is_indirection()
{
return detail::is_indirection<T>(true);
}
template <class T, bool ind = is_indirection<T>()>
struct underlying_type
{
using type = T;
};
template <class T>
struct underlying_type<T, true>
{
using type = typename std::remove_reference<decltype(*(std::declval<T>()))>::type;
};
template <class T>
typename std::enable_if<is_indirection<T>(), typename std::add_lvalue_reference<typename underlying_type<T>::type>::type>::type underlying_value(T&& val)
{
return *std::forward<T>(val);
}
template <class T>
typename std::enable_if<!is_indirection<T>(), T&>::type underlying_value(T& val)
{
return val;
}
template <class T>
typename std::enable_if<!is_indirection<T>(), const T&>::type underlying_value(const T& val)
{
return val;
}
template <class T>
class Storage
{
public:
T val;
void print()
{
std::cout << underlying_value(val) << '\n';
}
};
template <class T>
class StringStorage
{
public:
T str;
void printSize()
{
std::cout << underlying_value(str).size() << '\n';
}
};
int main()
{
int* a = new int(213);
std::string str = "some string";
std::shared_ptr<std::string> strPtr = std::make_shared<std::string>(str);
Storage<int> sVal{ 1 };
Storage<int*> sPtr{ a };
Storage<std::string> sStrVal{ str };
Storage<std::shared_ptr<std::string>> sStrPtr{ strPtr };
StringStorage<std::string> ssStrVal{ str };
StringStorage<const std::shared_ptr<std::string>> ssStrPtr{ strPtr };
sVal.print();
sPtr.print();
sStrVal.print();
sStrPtr.print();
ssStrVal.printSize();
ssStrPtr.printSize();
std::cout << is_indirection<int*>() << '\n';
std::cout << is_indirection<int>() << '\n';
std::cout << is_indirection<std::shared_ptr<int>>() << '\n';
std::cout << is_indirection<std::string>() << '\n';
std::cout << is_indirection<std::unique_ptr<std::string>>() << '\n';
}

Defining hash for vectors: template parameters not deducible in partial specialization

I am trying to define a hasher for vectors. I have a primary template for simple types, and a specialization for classes which have operator().
However, I get an error template parameters not deducible in partial specialization. Could somebody please point out why?
template <typename T> struct hash<vector<T>>
{
size_t operator()(const vector<T> &x) const
{
size_t res = 0;
for(const auto &v:x) {
boost::hash_combine(res,v);
}
return res;
}
};
template <typename T> struct hash<vector<enable_if_t<true_t<decltype(sizeof(declval<T>()()))>::value, T>>>
{
size_t operator()(const vector<T> &x) const
{
size_t res = 0;
for(const auto &v:x) {
boost::hash_combine(res,v());
}
return res;
}
};
I don't really like partial specialization here, especially as it causes code duplication.
template <typename T>
struct hash<vector<T>>
{
template<class T>
static auto call_if_possible(const T& t, int) -> decltype(t()) { return t(); }
template<class T>
static auto call_if_possible(const T& t, ...) -> decltype(t) { return t; }
size_t operator()(const vector<T> &x) const
{
size_t res = 0;
for(const auto &v:x) {
boost::hash_combine(res,call_if_possible(v, 0));
}
return res;
}
};
(If this hash is actually std::hash, then the answer is "don't do it". You may not specialize a standard library template unless the specialization depends on a user-defined type.)
In your second template specialization T inside the enable_if is in a non-deduced context, so the compiler cannot deduce it. It is effectively the same as:
template<typename T>
struct Identity
{
using type = T;
}
template<typename T>
void f(typename Identity<T>::type x){} // T is non-deducible
Moreover, you have a "double" non-deducible context, because an expression containing T inside a decltype is non-deducible.
for what it's worth, here was my first attempt - handles ADL-lookup of hash_value as well as the one in the boost namespace:
#include <iostream>
#include <boost/functional/hash.hpp>
#include <vector>
template<class T>
struct hash_value_defined
{
template<class U> static auto boost_hash_value_test(U*p) -> decltype(boost::hash_value(*p),
void(),
std::true_type());
template<class U> static auto adl_hash_value_test(U*p) -> decltype(hash_value(*p),
void(),
std::true_type());
template<class U> static std::false_type boost_hash_value_test(...);
template<class U> static std::false_type adl_hash_value_test(...);
static constexpr bool boost_value = decltype(boost_hash_value_test<T>(nullptr))::value;
static constexpr bool adl_value = decltype(adl_hash_value_test<T>(nullptr))::value;
static constexpr bool value = boost_value or adl_value;
};
template<class T, class A, std::enable_if_t<hash_value_defined<T>::value> * = nullptr >
size_t hash_value(const std::vector<T, A>& v) {
size_t seed = 0;
for(const auto& e : v) {
boost::hash_combine(seed, e);
}
return seed;
}
int main()
{
using namespace std;
vector<int> x { 1, 2, 3, 4, 5 };
auto h = hash_value(x);
cout << h << endl;
return 0;
}