Imagine some function (RetrieveResult), returning an object by pointer/reference/value - I don't know and don't want to know, because things may change. I just want to store the result, using auto and also protect that object from accidental changing in the current scope or, for example, if the object is propagated upwards.
It is quite intuitive just to write:
const auto result = RetrieveResult();
and everything works fine, if RetrieveResult returns an object by value or by reference. But if the function returns a pointer, constancy is applied to that pointer, not to the object the poiter points to. What way I still can change the object. Writing
const auto const result = ....
results in the compilation error:
duplicate 'const'
Of course, I can declare variable like this:
const auto* ...
const auto* const...
But that way ties me close to pointers, i.e. it isn't a universal solution.
Is it possible to preserve true constancy, and, in the same time, provide flexibility (independency of the concrete type)?
There is experimental support for this utility called std::propagate_const in the Library Fundamentals v2. You can write a type trait on top of that that does this for you (if you do not have std::propagate_const you can consider writing it yourself :))
namespace {
template <typename T, typename = std::enable_if_t<true>>
PropagateConst {
using type = T;
};
template <typename T>
PropagateConst<T, std::enable_if_t<std::is_same<
decltype(*std::declval<std::decay_t<T>>()),
decltype(*std::declval<std::decay_t<T>>())>::value>> {
using type = std::propagate_const_t<std::decay_t<T>>;
};
template <typename T>
using PropagateConst_t = typename PropagateConst<T>::type;
template <typename Type>
decltype(auto) propagate_const(Type&& in) {
return PropagateConst_t<std::add_rvalue_reference_t<Type>>{in};
}
} // <anonymous>
// then use it like this
const auto result = propagate_const(RetrieveResult());
Note that the solution I have above only checks for the presence of an operator* in the possible pointer type. You might want to consider writing a more extensive test for that.
Also note that this uses reference collapsing in the propagate_const example so expect at least a move to happen in cases where you might be expecting elision. You can optimize it based on your use case. I just thought I would outline what was in my head. Maybe that would help
template<class T>
struct very_const_t { using type=T; };
template<class T>
struct very_const_t<T*> { using type=typename very_const_t<T>::type const*; };
template<class T>
struct very_const_t<T&> { using type=typename very_const_t<T>::type const&; };
template<class T>
typename very_const_t<T>::type&&
very_const( T&& t ) { return std::forward<T>(t); }
then:
const auto result = very_const(RetrieveResult());
note that this can block elision. I carefully do not block move semantics, however.
This does not move const into smart pointers. If you want that:
template<class T, class D>
struct very_const_t<std::unique_ptr<T,D>> { using type=std::unique_ptr<typename very_const_t<T>::type const, D>; };
template<class T>
struct very_const_t<std::shared_ptr<T>> { using type=std::shared_ptr<typename very_const_t<T>::type const>; };
will do it for unique and shared.
Related
I write interfaces through concepts for implementation validation.
There are no problems with conventional methods:
// Interface realization
struct Realization
{
int* TestMethod(const std::string& aStr)
{
return (int *) aStr.c_str();
}
};
// Concept
template <typename T>
concept IRealization = std::is_same_v<decltype(&T::TestMethod), int* (T::*)(const std::string&)>;
// and then, for example
void Check()
{
static_assert(IRealization<Realization>)
}
but when I try to write a similar check for a template method:
// Interface realization
struct Realization
{
template <typename T>
int* TemplateMethod(const T& aStr)
{
return (int *) aStr.c_str();
}
};
, I run into a problem of dectype a template method, because I cant write
decltype(&RealizationImpl::TemplateMethod)
(at the time of checking the interface, I do not know the type that will be substituted)
Please tell me, can I somehow get the signature of the template function without type, or otherwise solve my problem? Thanks!
You should not write concepts like this. A concept should never check for something as specific as a member function with an exact signature. A concept should instead say that, given an instance of the type in question, I should be able to do i.memberFunc(...), where ... is the list of parameters.
For example, your "IRealization" concept (please don't prefix concepts with I. Concepts are not interfaces) ought to say "T must have a member function which can be called given a std::string argument and results in something which is convertible to an int." That would look like:
template <typename T>
concept IRealization = requires(T t, std::string str)
{
{ t.TestMethod(str) } -> convertible_to<int>;
};
This allows the user to provide a TestMethod that takes, for example, std::string_view instead of std::string. There's no point in being so incredibly restrictive on the type.
A concept that checks for T having a member function which is callable with some type U would have to be templated on both T and U:
template <typename T, typename U>
concept IRealization = requires(T t, U u)
{
{ t.TestMethod(u) } -> convertible_to<int>;
};
What is the problem with adding another type to the concept?
// Concept
template <typename T, typename U>
concept IRealization = std::is_same_v<decltype(&T::template TestMethod<U>), int* (T::*)(const U&)>;
For instance.
You could even make it prettier by creating a typedef -
template<typename T, typename U>
using FuncT = decltype(&T::template TestMethod<U>);
When implementing a custom container I came to the point where I needed to implement iterators. Of course I didn't want to write the code twice for const and non-const iterator. I found this question detailing a possible implementation like this:
template<class T>
class ContainerIterator {
using pointer = T*;
using reference = T&;
...
};
template<class T>
class Container {
using iterator_type = ContainerIterator<T>;
using const_iterator_type = ContainerIterator<const T>;
}
But I also found this this question that uses a template parameter:
template<class T, bool IsConst>
class ContainerIterator {
using pointer = std::conditional_t<IsConst, const T*, T*>;
using reference = std::conditional_t<IsConst, const T&, T&>;
...
};
template<class T>
class Container {
using iterator_type = ContainerIterator<T, false>;
using const_iterator_type = ContainerIterator<T, true>;
}
The first solution seems to be easier, but the answer is from 2010.
After doing some research, it seems, that the first version is not widely used, but I can't see why. I feel like I'm missing some obvious flaw of the first version.
So the questions become:
Are there any problems with the first version?
If no, why version #2 seems to be the preferred way in c++17? Or why should I prefer one over the other?
Also, yes, it would be a solution to use const_cast or simply duplicate the whole code. But I don't like those two.
The point of the second implementation is something you didn't copy: to make it easy to implement a specific requirement. Namely, an iterator must be implicitly convertible to a const_iterator.
The difficulty here lies in preserving trivial copyability. If you were to do this:
template<class T>
class ContainerIterator {
using pointer = T*;
using reference = T&;
...
ContainerIterator(const ContainerIterator &) = default; //Should be trivially copyable.
ContainerIterator(const ContainerIterator<const T> &) {...}
};
That doesn't work. const const Typename resolves down to const Typename. So if you instantiate ContainerIterator with a const T, then you now have two constructors that have the same signature, one of which is defaulted. Well, this means that the compiler will ignore your defaulting of the copy constructor and thus use you non-trivial copy constructor implementation.
That's bad.
There are ways to avoid this by using some metaprogramming tools to detect the const-ness of the T, but the easiest way to fix it is to specify the const-ness as a template parameter:
template<class T, bool IsConst>
class ContainerIterator {
using pointer = std::conditional_t<IsConst, const T*, T*>;
using reference = std::conditional_t<IsConst, const T&, T&>;
...
ContainerIterator(const ContainerIterator &) = default; //Should be trivially copyable.
template<bool was_const = IsConst, class = std::enable_if_t<IsConst || !was_const>>>
ContainerIterator(const ContainerIterator<T, was_const> &) {...}
};
Templates are never considered to be copy constructors, so this won't interfere with trivial copyability. This also uses SFINAE to eliminate the converting constructor in the event that it is not a const iterator.
More information about this pattern can be found as well.
I have been implementing several data structure classes that are supposed to be able to hold any type and lately I've been 'upgrading' them with type traits.
I wanted to implement a type trait that prevents very large values from being taken as a hard copy. For example in:
template<typename T>
T Max(T a, T b){ return (a < b) ? b : a; }
I would not want to take T as a value if T happens to by some chance would happen to have an outrageous size.
Instead I would want something like:
template<typename T>
T Max(ref_if_large<T>::val a, ref_if_large<T>::val b){ return (a < b) ? b : a; }
I tried implementing this myself, but I've gotten myself into one of those moments where I'm just not seeing what's wrong.
template<typename T, bool cond>
struct ref_if_true {
using V = T;
};
template<typename T>
struct ref_if_true<T, true> {
using V = T&;
};
template<typename T>
struct ref_if_large {
using val =
ref_if_true< T, ((sizeof(T)) > (12U))>::V;
};
The compiler complains about V not being defined but I can't honestly figure out why.
In the last definition add typename for the dependent type, like this: using val = typename ref_if_true<blah>::V;. But seriously, don't do this for a max function. There are enough problems with aliasing already, don't add uncertainty about whether there's going to be aliasing or not.
The Boost library's boost::call_traits<T>::param_type already does the “best param type” thing.
If one wants to avoid a dependency on Boost then of course it can make sense to invent this again (I've done it myself).
But, it's useful to at least be aware of the Boost functionality.
You need to add typename to tell the compiler that V is indeed a type.
template<typename T>
struct ref_if_large {
using val =
typename ref_if_true< T, ((sizeof(T)) > (12U))>::V;
};
By the way, consider using const references for large types. They'll allow binding to const objects and rvalues.
You could also use an alias template to avoid sprinkling the client code with typenames:
template<typename T>
using ref_if_large = typename ref_if_true<T, ((sizeof(T)) > (12U))>::V;
And if you're taking by reference, you might as well parametrize the return type.
template<typename T>
ref_if_large<T> Max(ref_if_large<T> a, ref_if_large<T> b){
return (a < b) ? b : a;
}
The specific problem is adequately answered by the other two answers here. It's worth additionally pointing out that ref_if_true is a specific version of a type trait that already exists in the standard library: std::conditional:
template <class T, bool cond>
using ref_if_true_t = std::conditional_t<cond, T&, T>;
and, as such, doesn't need to exist to begin with:
template<typename T>
using ref_if_large_t = std::conditional_t<(sizeof(T) > 12U), T&, T>;
or really probably:
template<typename T>
using ref_if_large_t = std::conditional_t<(sizeof(T) > 12U), T const&, T>;
since reference to non-const implies something different.
Note: metaprogramming is already hard. It gets harder if you blaze your own trail with your own naming conventions. Metafunctions that yield a type should name it type (not V or val).
My aim is to write std::variant, may be not full blown, but at least with fully working constructor/destructor pair and std::get<>() function.
I tried to reserve a memory using char array. The size of it is determined by the biggest type, which is found by using find_biggest_size<>() function. The constructor uses static assert, because it performs check if the type is in the list of specified types. For now, the constructor and in place constructor works.
template <typename ... alternatives>
class variant
{
char object[find_biggest_size<alternatives...>::value];
public:
template <typename T>
variant(T&& other)
{
static_assert(is_present<T, alternatives...>::value, "type is not in range");
new ((T*)&object[0]) T(std::forward<T>(other));
}
template <typename T, typename ... ArgTypes>
variant(in_place_t<T>, ArgTypes&& ... args)
{
static_assert(is_present<T, alternatives...>::value, "type is not in range");
new ((T*)&object[0]) T(std::forward<ArgTypes>(args)...);
}
~variant()
{
// what to do here?
}
};
Then I've stumbled upon a problem. I don't know what destructor to execute when the object dies. On top of that, it is impossible to access the underlying object, since I can't specialize std::get<>() to get the right type.
My question is: how to store the type after the creation of the object? Is it the right approach? If not, what should I use?
EDIT:
I tried to apply the comments. The problem is that the index of the type that is currently alive can't be constexpr, thus I can't extract the needed type from type list and invoke appropriate destructor.
~variant()
{
using T = typename extract<index, alternatives...>::type;
(T*)&object[0]->~T();
}
EDIT:
I've made a baseline implementation. It works, but has lots of missing features. You can find it here. I would be glad to receive a review, but please first read how do I write a good answer?.
How I'd probably start:
#include <iostream>
#include <utility>
#include <array>
template<class...Types>
struct variant
{
variant() {}
~variant()
{
if (type_ >= 0)
{
invoke_destructor(type_, reinterpret_cast<char*>(std::addressof(storage_)));
}
}
template<class T> static void invoke_destructor_impl(char* object)
{
auto pt = reinterpret_cast<T*>(object);
pt->~T();
}
static void invoke_destructor(int type, char* address)
{
static const std::array<void (*)(char*), sizeof...(Types)> destructors
{
std::addressof(invoke_destructor_impl<Types>)...
};
destructors[type](address);
}
std::aligned_union_t<0, Types...> storage_;
int type_ = -1;
};
int main()
{
variant<int, std::string> v;
}
First of all, you need to know which object is currently in the variant. If you want to get a type from it, that is not currently in it, you must throw an exception.
For the storage I use a union (as I do here to make it constexpr); you can't use the placement new operator as a constexpr so I think the union is the only actual way to do that (which means the only one I came up with). Mind you: you still need to explicitly call the destructor. Which yields the strange workaround I have, because a type used in a constexpr must be trivially destructible.
Now: you can implement a class similar to find_biggest_size which gives you the type from an int as a template parameter. I.e. something like that (incomplete example) :
template<int idx, typename ...Args>
struct get_type;
template<int idx, typename First, typename ...Rest>
struct get_type<idx, First, Rest...>
{
using type = typename get_type<idx-1, Rest>::type;
};
template<typename First, typename ...Rest>
struct get_type<0, First, Rest...>
{
using type = First;
};
//plus specialization without Rest
And then you can implement the get function:
template<int i, typename ...Args>
auto get(variant<Args...> & v) -> typename get_type<i, Args...>::type
{ /* however you do that */ }
I hope that helps.
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