c++ rvalue reference and const qualifier - c++

Among the many benefits of const qualification is to make an API more understandable, example:
template<typename T> int function1(T const& in);
// clearly, the input won’t change through function1
With the introduction of rvalue references, one can benefit from perfect forwarding but often const qualifiers are removed, example:
template<typename T> int function2(T&& in);
// can explicitly forward the input if it's an rvalue
Apart from documentation, is there a good way to describe that function2 won’t change its input?

template<typename T> int function2(T&& in);
// can explicitly forward the input if it's an rvalue
Apart from documentation, is there a good way to describe that
function2 won’t change its input?
Yes. Stick with the C++03 solution:
template<typename T> int function1(T const& in);
// clearly, the input won’t change through function1
The benefits of perfect forwarding are that you don't want to assume if something is const or non-const, lvalue or rvalue. If you want to enforce that something is not modified (i.e. that it is const), then explicitly say so by adding const.
You could do this:
template<typename T> int function1(T const&& in);
// clearly, the input won’t change through function1
However everyone who read your code would wonder why you've used rvalue references. And function1 would cease to accept lvalues. Just use const & instead and everyone will understand. It is a simple and well understood idiom.
You don't want to perfectly forward. You want to enforce immutability.

You could say this:
template <typename T>
typename std::enable_if<immutable<T>::value, int>::type
function(T && in)
{
// ...
}
where you have something like:
template <typename T> struct immutable
: std::integral_constant<bool, !std::is_reference<T>::value> {};
template <typename U> struct immutable<U const &>
: std::true_type {};
This way, the template will only be usable if the universal reference is either a const-reference (so T = U const &) or an rvalue-reference (so T is not a reference).
That said, if the argument is not going to be changed, you could just use T const & and be done with it, since there's nothing to be gained from binding mutably to temporary values.

Related

Conversion using std::decay_t

I have some code but I do not understand what does it do
template <typename T, typename U = T>
struct MyStruct
{
};
template <typename T>
[[nodiscard]] inline T fromValue(const QJsonValue& json)
{
return MyStruct<std::decay_t<T>>::get(json);
}
I do not understand what heppenes in line
return MyStruct<std::decay_t<T>>::get(json);
And Why we use empty struct?
You use std::decay_t in order to obtain the basic type without cv-qualification (plus some fewer used special features for arrays and function pointers). Starting from C++20, for most cases it should be sufficient to use std::remove_cvref_t<T> for this.
The function signature
template <typename T>
[[nodiscard]] inline T fromValue(const QJsonValue& json)
{
return MyStruct<std::decay_t<T>>::get(json);
}
basically tells you, that regardless of the cv-qualification (T, T&, T const&, T volatile const&, etc.) the class template should be instantiated only with the base type T. Thereafter, the static function get is called -- static because those are functions that can be called without an object.
EDIT: sorry, I just recognized that the struct is empty. So your code won't work, as there simply is no get function available.
Note however, that there is a danger of returning a dangling reference here, because for T& as template parameter, you return a reference. Moreover, you don't want consts, volatiles, etc. in your return type. Therefore I would question the design of this function and use it with caution.

Make a legacy function f(T&) accept a temporary of a particular type

I have a legacy function that takes an argument by T&, in modern C++ this should have been a T&& from the start.
e.g.
namespace old_space {
template<class T>
void f(T& t) {...}
}
Currently I have to do this for it to work,
MyType v = g(1);
old_space::f(v);
The function refuses to take, even in principle, a temporary rvalue.
old_space::f(g(1));
I know that there can be no leaks or dangling references and it would be actually ok to accept an rvalue.
(This is because the very nature of MyType, which a simulated reference.)
I can't overload f, in the original or any namespace.
Also, I want a solution that works for MyType only and not for any random type.
It occurred to me that I could have conversion operator to an lvalue.
struct MyType {
...
operator MyType& operator()&& {return *this;}
};
I though it was an elegant idea, however it didn't work (f can't still capture f(g(1)).
Also the compiler complained that error: converting ‘MyType’ to a reference to the same type will never use a type conversion operator [-Werror=class-conversion] if I activate warnings, which is probably true but I don't know why also.
The question is, can I adapt struct MyType in any way so that f(g(1)) is valid syntax, where g(1) generates a new value of MyType and f's signature is template<class T> void f(T&).
I also tried this, but still didn't work:
template<class T, std::enable_if_t<std::is_same<T, MyType>::value, int> =0>
operator T&() && {return *this;}
You can create an overload:
void f(MyType&& t) { f(t); } // forward to lvalue version.

CV-qualifiers & references for concept requires parameters

Suppose I have a deserialize function:
template <typename T>
T deserialize(const std::string& src);
// Specialized for various types
// ...
and want to define a Deserializable concept for types for which deserialize is specialized. Are there any difference between the following versions?
template <typename T>
concept Deserializable = requires(std::string s) { deserialize<T>(s); };
// concept Deserializable = requires(std::string& s) { deserialize<T>(s); };
// concept Deserializable = requires(const std::string s) { deserialize<T>(s); };
// concept Deserializable = requires(const std::string& s) { deserialize<T>(s); };
If requires was similar to a normal function, then I'd expect the 1st (resp. 3rd) version to be identical to the 2nd (resp. 4th) version, as the name s inside a normal function body is an lvalue. However, I've seen the standard library use reference types as requires parameters, for example here:
template< class T >
concept range = requires(T& t) {
ranges::begin(t); // equality-preserving for forward iterators
ranges::end (t);
};
which seems to suggest that reference vs. value matters.
More generally, do CV-qualifiers and references for requires parameters matter?
UPDATE: I came up with a test to show that requires indeed seems to behave like normal functions:
template <typename T>
void f(T&&, T&&) {}
template <typename T>
concept Foo = requires(int x, int& y, int&& z) {
f(x, y);
f(x, z);
f(x, 123); // won't compile: deduced int& and int
};
int main()
{
}
So it seems the requires(T& t) in the standard library example above is no different than requires(T t). Is this always the case?
If T is an rvalue reference type U&&, T& is U&, but that matters only if decltype or something like std::forward is used. The only other formal difference is that it prevents the parameter from being adjusted to be a pointer if it’s an array or function type, which also rarely matters.
It may also be meant to avoid suggesting that a type is copyable/movable (as is often required for by-value function parameters).

Deducing the template parameter of a templated class argument: const issue

I think the problem is fairly common, so there should be a known solution. I came up with one, but I'm not really satisfied, so I'm asking here, hoping someone can help.
Say I have a function, whose signature is
template<typename T>
void foo(const MyArray<const T>& x);
The const in the template parameter is to prevent me from changin the array content, since (for reasons beyond this question), the accessors ([] and ()) of MyArray<T> are always marked const, and return references to T (hence, the const ensure safety, since MyArray<T>::operator[] returns T&, while MyArray<const T>::operator[] returns const T&).
Great. However, templates with different template arguments are non related, so I can't bind a reference to MyClass<T> to a reference of MyClass<const T>, meaning I can't do this
MyArray<double> ar(/*blah*/);
foo(ar);
Notice that, without a reference, the code above would work provided that there is a copy constructor that lets me create MyArray<const T> from MyArray<T>. However, I don't want to remove the reference, since the array construction would happen a lot of times, and, despite relatively cheap, its cost would add up.
So the question: how can I call foo with an MyArray<T>?
My only solution so far is the following:
MyArray<T> ar(/*blah*/);
foo(reinterpret_cast<MyArray<const T>>(ar));
(actually in my code I hid the reinterpret cast in an inlined function with more verbose name, but the end game is the same). The class MyArray does not have a specialization for const T that makes it not reinterpretable, so the cast should be 'safe'. But this is not really a nice solution to read. An alternative, would be to duplicate foo's signature, to have a version taking MyArray<T>, which implementation does the cast and calls the const version. The problem with this is code duplication (and I have quite a few functions foo that need to be duplicated).
Perhaps some extra template magic on the signature of foo? The goal is to pass both MyArray<T> and MyArray<const T>, while still retaining const-correctness (i.e., make the compiler bark if I accidentally change the input in the function body).
Edit 1: The class MyArray (whose implementation is not under my control), has const accessors, since it stores pointers. So calling v[3] will modify the values in the array, but not the members stored in the class (namely a pointer and some smart-pointer-like metadata). In other words, the object is de facto not modified by accessors, though the array is. It's a semantic distinction. Not sure why they went this direction (I have an idea, but too long to explain).
Edit 2: I accepted one of the two answers (though they were somewhat similar). I am not sure (for reasons long to explain) that the wrapper class is doable in my case (maybe, I have to think about it). I am also puzzled by the fact that, while
template<typename T>
void foo(const MyArray<const T>& x);
MyArray<int> a;
foo(a);
does not compile, the following does
void foo(const MyArray<const int>& x);
MyArray<int> a;
foo(a);
Note: MyArray does offer a templated "copy constructor" with signature
template<typename S>
MyArray(const MyArray<S>&);
so it can create MyArray<const T> from MyArray<T>. I am puzzled why it works when T is explicit, while it doesn't if T is a template parameter.
I would stay with
template<typename T>
void foo(const MyArray<T>&);
and make sure to instantiate it with const T (in unitTest for example).
Else you can create a view as std::span.
Something like (Depending of other methods provided by MyArray, you probably can do a better const view. I currently only used operator[]):
template <typename T>
struct MyArrayConstView
{
MyArrayConstView(MyArray<T>& array) : mArray(std::ref(array)) {}
MyArrayConstView(MyArray<const T>& array) : mArray(std::ref(array)) {}
const T& operator[](std::size_t i) {
return std::visit([i](const auto& a) -> const T& { return a[i]; }), mArray);
}
private:
std::variant<std::reference_wrapper<MyArray<T>>,
std::reference_wrapper<MyArray<const T>>> mArray;
};
and then
template <typename T>
void foo(const MyArrayConstView<T>&);
but you need to call it explicitly, (as deduction won't happen as MyArray<T> is not a MyArrayConstView)
MyArray<double> ar(/*blah*/);
foo(MyArrayConstView{ar});
foo<double>(ar);
Since you are not allowed to change MyArray, one option is to use an adapter class.
template <typename T>
class ConstMyArrayView {
public:
// Not an explicit constructor!
ConstMyArrayView(const MyArray<T>& a) : a_(a) {}
const T& operator[](size_t i) const { return a_[i]; }
private:
const MyArray<T>& a_;
};
template<typename T>
void foo(const ConstMyArrayView<T>& x);
MyArray<T> x;
foo(x);
But in the end, if you can change MyArray to match the const-correctness you want, or switch to a class that does, that'll be the better option.
Here's an ugly but effective way to have a function use one type, but also get the compiler to check that the same code would compile if it used a different type instead:
template <typename From, typename To>
struct xfer_refs_cv
{
using type = To;
};
template <typename From, typename To>
struct xfer_refs_cv<const From, To>
{
using type = const typename xfer_refs_cv<From, To>::type;
};
template <typename From, typename To>
struct xfer_refs_cv<volatile From, To>
{
using type = volatile typename xfer_refs_cv<From, To>::type;
};
template <typename From, typename To>
struct xfer_refs_cv<From&, To>
{
using type = typename xfer_refs_cv<From, To>::type&;
};
template <typename From, typename To>
struct xfer_refs_cv<From&&, To>
{
using type = typename xfer_refs_cv<From, To>::type&&;
};
template <typename CheckType, typename Func, typename CallType>
constexpr decltype(auto) check_and_call(Func&& f, CallType&& call_arg)
noexcept(noexcept(std::forward<Func>(f)(std::forward<CallType>(call_arg))))
{
(void) decltype(std::declval<Func&&>()
(std::declval<typename xfer_refs_cv<CallType&&, CheckType>::type>()), 0){};
return std::forward<Func>(f)(std::forward<CallType>(call_arg));
}
template<typename T>
void foo(const MyArray<T>& x)
{
check_and_call<MyArray<const T>>(
[](auto&& x) {
// Function implementation here.
}, x);
}

Factory class perfect forwarding

Consider following code sample:
template<class T, class U>
struct b{
b(T &&, U &&);
//some useful stuff
};
template<class T>
struct factory{
factory(T &&arg) : arg_(std::forward<T>(arg)){}
template<class U>
auto create(U &&u) const &{
return b<const T &, U>(arg_, std::forward<U>(u));
}
template<class U>
auto create(U &&u) &&{
return b<T, U>(std::forward<T>(arg_), std::forward<U>(u));
}
T arg_;
};
template<class T>
auto
make_factory(T &&t){
return factory<T>(std::forward<T>(t));
}
I am concerned with code inside factory struct - are the create methods fastest possible, is it the best way to use perfect forwarding with factory classes?
Did I miss something or this code is 'perfect' in terms of forwarding/moving data around? I would like to avoid copying wherever it is possible.
Should I add/remove const in any of the create methods?
EDIT: Due to Your concerns - I am aware that sometimes I will pass lvalue reference and specialize template for it. It is desired behaviour. As I written up there - I want to avoid copying anything unless really needed. So factory can store reference if object was created in scope outside of it. I know it is unsafe, but still it is a design choice. For reference factory created object should also store reference. I hope this clarifies most of Your issues.
I don't know whether you fully understand what you are doing in the code.
template<class T>
auto
make_factory(T &&t){
return factory<T>(std::forward<T>(t));
}
This code try to make a factory of T. However, if you pass an lvalue to make_factory(), T will be deduced to a reference. Then you will create a factory of reference. I think this is unlikely your purpose.
And moreover, this one
factory(T &&arg) : arg_(std::forward<T>(arg)){}
is not a forwarding reference, because T is not the type name to be deduced in this function.