Suppose I have a template class, for example
template<int n, int m> class Matrix
Is there a way to define a matrix multiplication operator * so that
the arguments to * can be either lvalues or rvalue references
*infers the appropriate return type (i.e. the appropriate template parameters) from its arguments
What I have in mind is something like
template< int n,int k, int m, template<int,int> class T1, template<int, int> class T2, template<int,int>class T3 >
T3<n,m> operator*(T1<n,k>&&, T2<k,m>&&)//does not work
When I try to run the above code (with the bodies filled in in the obvious ways), I get an error:
Cannot convert from Matrix<1,1> to Matrix<1,1>&&
when the arguments are lvalues.
Yes. From my own code:
template
<
int LeftColumnsRightRows, int LeftRows,
int RightColumns
>
Matrix<RightColumns, LeftRows> operator*(Matrix<LeftColumnsRightRows, LeftRows> const& a, Matrix<RightColumns, LeftColumnsRightRows> const& b)
And I don't know why you'd want it to take &&s. If you want to convert two other types to matrices and then multiply them, you should do the conversion outside of the multiplication operator.
I would simply stick to const references as well, as explained by previous answer. But to clarify why your code doesn't work, perfect forwarding only applies when you use an rvalue reference to an cv-unqualified template parameter. In layman's terms, it has to be just T&&, where T is a function template parameter:
template<class T>
void ForwardMe(T&& t)
{
DoSomething(std::forward<T>(t));
}
The idea is that the compiler will be able to deduce T as type& when passed a lvalue (so the function signature becomes void ForwardMe(type&) because of reference collapsing rules), or just type in case of rvalues (signature becomes void ForwardMe(type&&)).
In your example, you do something like:
template<int N, template<int> class T>
void ForwardMe(T<N>&& t)
{
// ...
}
This doesn't work as you had expected, because the compiler cannot deduce T to be a reference to something, so you can't have perfect forwarding. The function parameter t will therefore only match rvalue references.
Since const references can bind to temporaries, using const T<N>& instead in above example will solve your problems. But if you really want to support both lvalue and rvalue inputs (because you like to have move semantics where appropriate), you have two options:
Write overloads for all 4 permutations: lvalue*lvalue, lvalue*rvalue, rvalue*lvalue, rvalue*rvalue.
Write a generic function template and use SFINAE to limit the input types.
The latter would be something like:
#include <type_traits>
template<class L, class R>
struct MatrixMulResult_helper;
template<int n, int m, int k, template<int, int> class T>
struct MatrixMulResult_helper<T<n, m>, T<m, k>> { using type = T<n, k>; };
template<class L, class R>
using MatrixMulResult = typename MatrixMulResult_helper<L, R>::type;
template<class L, class R>
MatrixMulResult<std::decay_t<L>, std::decay_t<R>>
operator*(L&& lhs, R&& rhs)
{
// ...
}
The compiler is now free to deduce L and R as references. MatrixMulResult<> makes sure that this function is only defined when (the decayed types of) L and R are respectively of the form T<n,m> and T<m,k>. It returns a T<n,k>.
Related
With forwarding functions using a template parameter, the idiom is to forward using the typename T, e.g.
template <typename T>
void fwd(T &&x) {
some_func(std::forward<T>(x));
}
Equivalent approach when using a forwarding function (or lambda) that takes an auto type parameter:
void fwd(auto &&x) {
some_func(std::forward<decltype(x)>(x));
}
In the above decltype(x) is not necessarily the same as the type of T in the previous example. E.g. fwd(3) deduces T to int while decltype(x) is int&&.
Can someone explain why we use T in one case vs. decltype(x) as the template parameter to std::forward above? Is the end result always guaranteed to be identical based on the implementation of std::forward?
void fwd(auto &&x) is a c++20 shorthand for template <typename T> void fwd(T &&x). But in this case you don't have type to refer to. You can write this:
void fwd(auto &&x) {
using T = std::remove_reference_t<decltype(x)>;
some_func(std::forward<T>(x));
}
Here T equals to T in original version, but it doesn't matter much.
std::forward<T> and std::forward<std::remove_reference_t<T>> are equal, because its signature defined like this:
template< class T >
constexpr T&& forward( std::remove_reference_t<T>&& t ) noexcept;
I have a question about the code written in https://koturn.hatenablog.com/entry/2018/06/10/060000
When I pass a left value reference, if I do not remove the reference with std::decay_t, I get an error.
Here is the error message
'error: 'operator()' is not a member of 'main()::<lambda(auto:11, int)>&
I don't understand why it is necessary to exclude the left value reference.
I would like to know what this error means.
#include <iostream>
#include <utility>
template <typename F>
class
FixPoint : private F
{
public:
explicit constexpr FixPoint(F&& f) noexcept
: F{std::forward<F>(f)}
{}
template <typename... Args>
constexpr decltype(auto)
operator()(Args&&... args) const
{
return F::operator()(*this, std::forward<Args>(args)...);
}
}; // class FixPoint
namespace
{
template <typename F>
inline constexpr decltype(auto)
makeFixPoint(F&& f) noexcept
{
return FixPoint<std::decay_t<F>>{std::forward<std::decay_t<F>>(f)};
}
} // namespace
int
main()
{
auto body = [](auto f, int n) -> int {
return n < 2 ? n : (f(n - 1) + f(n - 2));
};
auto result = makeFixPoint(body)(10);
std::cout << result << std::endl;
}
I don't understand why it is necessary to exclude the left value
reference. I would like to know what this error means.
When you pass an lvalue lambda into makeFixPoint(), the template parameter F of makeFixPoint() is instantiated as L&, where L is the lambda type. In the function body, FixPoint<std::decay_t<F>>{...} will be instantiated as FixPoint<L>{...}, so the constructor of FixPoint is instantiated as
explicit constexpr FixPoint(L&& f);
which accepts a lambda type of rvalue reference. In makeFixPoint(), if you initialize it with {std::forward<F>(f)} i.e. {std::forward<L&>(f)}, f will be forwarded as an lvalue, which will be ill-formed since rvalue reference cannot bind an lvalue.
The purpose of using {std::forward<std::decay_t<F>>(f)} is to force f to be forwarded as an rvalue.
,The code you have shared is toxic.
template <typename F>
class FixPoint : private F
{
public:
explicit constexpr FixPoint(F&& f)
what this means is that we expect F to be a value type (because inheriting from a reference isn't possible). In addition, we will only accept rvalues -- we will only move from another F.
: F{std::forward<F>(f)}
{}
this std::forward<F> is pointless; this indicates that the person writing this code thinks they are perfect forwarding: they are not. The only legal types F are value types (not references), and if F is a value type F&& is always an rvalue reference, and thus std::forward<F> is always equivalent to std::move.
There are cases where you want to
template<class X>
struct wrap1 {
X&& x;
wrap1(X&& xin):x(std::forward<X>(xin)){}
};
or even
template<class X>
struct wrap2 {
X x;
wrap2(X&& xin):x(std::forward<X>(xin)){}
};
so the above code is similar to some use cases, but it isn't one of those use cases. (The difference here is that X or X&& is the type of a member, not a base class; base classes cannot be references).
The use for wrap2 is when you want to "lifetime extend" rvalues, and simply take references to lvalues. The use for wrap1 is when you want to continue the perfect forwarding of some expression (wrap1 style objects are generally unsafe to keep around for more than a single line of code; wrap2 are safe so long as they don't outlive any lvalue passed to them).
template <typename F>
inline constexpr decltype(auto)
makeFixPoint(F&& f) noexcept
{
return FixPoint<std::decay_t<F>>{std::forward<std::decay_t<F>>(f)};
}
Ok more red flags here. inline constexpr is a sign of nonsense; constexpr functions are always inline. There could be some compilers who treat the extra inline as meaning something, so not a guarantee of a problem.
return FixPoint<std::decay_t<F>>{std::forward<std::decay_t<F>>(f)};
std::forward is a conditional move. It moves if there is an rvalue or value type passed to it. decay is guaranteed to return a value type. So we just threw out the conditional part of the operation, and made it into a raw move.
return FixPoint<std::decay_t<F>>{std::move(f)};
this is a simpler one that has the same meaning.
At this point you'll probably see the danger:
template <typename F>
constexpr decltype(auto)
makeFixPoint(F&& f) noexcept
{
return FixPoint<std::decay_t<F>>{std::move(f)};
}
we are unconditionally moving from the makeFixPoint argument. There is nothing about the name makeFixPoint that says "I move from the argument", and it accept forwarding references; so it will silently consume an lvalue and move-from it.
This is very rude.
So a sensible version of the code is:
template <typename F>
class FixPoint : private F
{
public:
template<class U,
class dU = std::decay_t<U>,
std::enable_if_t<!std::is_same_v<dU, FixPoint> && std::is_same_v<dU, F>, bool> = true
>
explicit constexpr FixPoint(U&& u) noexcept
: F{std::forward<U>(u)}
{}
[SNIP]
namespace
{
template <typename F>
constexpr FixPoint<std::decay_t<F>>
makeFixPoint(F&& f) noexcept
{
return FixPoint<std::decay_t<F>>{std::forward<F>(f)};
}
} // namespace
that cleans things up a bit.
The template just can't work as intended this way.
First, because it is supposed to be possible to inherit from F, it doesn't make much sense to use std::decay_t. Instead it is probably supposed to be just std::remove_cvref_t, but before C++20 that was kind of cumbersome to write and std::decay_t does almost the same.
In the class template F is intended to be a non-reference type. It is not a template parameter of the constructor. So it cannot be used for perfect-forwarding. The constructor argument will always be a rvalue reference which cannot take lvalues.
Even if a lvalue is passed to makeFixPoint, the call std::forward<std::decay_t<F>>(f) forces conversion to a rvalue reference, which then can be used in the constructor. That is clearly dangerous. The function always behaves as if you had std::moved the argument into it.
It should just be std::forward<F>(f) and the constructor should take another template parameter that can be used to form a forwarding reference:
template<typename G>
requires std::is_same_v<std::remove_cvref_t<G>, F>
explicit constexpr FixPoint(G&& g) noexcept
: F{std::forward<G>(g)}
{}
Consider the following code in C++14, following the posts here, here and here:
// Include
#include <tuple>
#include <iostream>
#include <type_traits>
// Temporary function queue declaration
template <class... F>
class temporary_function_queue;
// Apply function queue declaration
template <class... F>
constexpr temporary_function_queue<F&&...> apply_function_queue(F&&... f);
// Temporary function queue definition
template <class... F>
class temporary_function_queue final
{
// Types
private:
using const_lvalue_reference = const temporary_function_queue&;
using rvalue_reference = temporary_function_queue&&;
using temporary_type = temporary_function_queue<F&&...>;
using data_type = std::tuple<F&&...>;
// Lifecycle
private:
temporary_function_queue(rvalue_reference) = default;
temporary_function_queue(const_lvalue_reference) = delete;
temporary_function_queue operator=(rvalue_reference) = delete;
temporary_function_queue operator=(const_lvalue_reference) = delete;
explicit constexpr temporary_function_queue(F&&... f)
: _f{std::forward<F>(f)...}
{
}
// Temporary creator
public:
friend constexpr temporary_type apply_function_queue<>(F&&... f);
// Apply function queue declaration
public:
template <class... Args>
decltype(auto) operator()(Args&&... args) const&&
{
// Do I need to do std::forward on f0 too? If so, how?
return std::get<0>(_f)(std::forward<Args>(args)...);
}
// Data members
private:
data_type _f;
};
// Apply function queue definition
template <class... F>
constexpr temporary_function_queue<F&&...> apply_function_queue(F&&... f)
{
return temporary_function_queue<F&&...>(std::forward<F>(f)...);
}
/* Example of use
int main(int argc, char* argv[])
{
apply_function_queue(
[](auto i){std::cout<<0<<std::endl;},
[](auto i){std::cout<<1<<std::endl;}
)(0);
return 0;
}
*/
The goal is to produce the following call syntax:
apply_function_queue(f0, f1, f2)(a, b, c, d, e);
Where f0, f1, f2 are either function pointers, functors, lambdas..., and where a, b, c, d, e are arguments to be perfectly forwarded. This function should produce a temporary type, and then call the operator() of that temporary type, and this operator should do a perfect forwarding of fn (for now f0, it will be changed later) with the arguments a, b, c, d, e.... The temporary_function_queue should not be usable in any other context.
The problem is that I am a little lost with the forwarding, universal references and lvalue references... Is the code shown above safe? If not what example of use would lead to undefined behaviour? And in that case, how to make it safe, and efficient (ideally, I would like no runtime overhead on most compilers with -O3)?
Note: Dangling references are quite likely to happen with this approach.
auto make_f(); // return by value
auto&& q = apply_function_queue(make_f());
// q holds dangling rvalue reference
q(a, b, c); // whoops...
First some remarks about wording and deduction. Let:
template<class... T> void f(T&&... p) {}
Note: When this template is instantiated, there are two different packs here: T... and T&&....
Call it with an lvalue or type R and an rvalue of type Q:
R a;
f(a, Q{});
Now T... will be R&, Q but T&&... will be R&, Q&&.
Forwarding the pack p will result in the T&&... pack.
'decltype'(std::forward<T>(p)...) === T&&...
(Note: You can't actually apply decltype here - it's just for illustration.)
Therefore, I'll call the pack that is actually deduced (T...) the deduced types/pack and the result of adding rvalue references / forwarding (T&&...) the forwarded types/pack.
Applying && everywhere inside the class as well as in the return type of apply_function_queue is superfluous. (If you return temporary_function_queue<F&&...> from apply_function_queue there is no need for && inside temporary_function_queue. And if you apply && inside the class everywhere there is no need to return temporary_function_queue<F&&...>.)
You either instantiate the class template with the deduced pack and add && everywhere you want references our you instantiate the class template with the forwarding pack and don't add &&.
It is required to have the deduced types available in the class. (Because the friend declaration uses both F... and F&&....) So you'll want to remove && from the return type of apply_function_queue.
You'll need to change some declarations:
apply_function_queue
forward declaration:
template <class... F>
constexpr temporary_function_queue<F...> apply_function_queue(F&&... f);
definition:
template <class... F>
constexpr temporary_function_queue<F...> apply_function_queue(F&&... f)
{
return temporary_function_queue<F...>(std::forward<F>(f)...);
}
temporary_type
The class instance type is temporary_function_queue<F...> not temporary_function_queue<F&&...>!
using temporary_type = temporary_function_queue<F...>;
friend declaration
friend constexpr temporary_type apply_function_queue<F...>(F&&... f);
If you want perfect forwarding of rvalue function types in the function call operator you'll have to resort to manual casting / forwarding I think.
Inside decltype(auto) operator()(Args&&... args) const&& you'll find that
decltype(std::get<0>(_f)) === std::tuple_element_t<0u, data_type>&
which by reference collapsing rules is a lvalue reference. What you actually want, in order to forward the elements from the tuple is the tuple_element::type.
Thus you'd have to directly cast to the actual type in the tuple:
return static_cast<std::tuple_element_t<0u, data_type>>(
std::get<0>(_f))(std::forward<Args>(args)...);
or forward (which will have the same effect through reference collapsing):
return std::forward<std::tuple_element_t<0u, data_type>>(
std::get<0>(_f))(std::forward<Args>(args)...);
I have something like:
template <class Foob, class T>
void do_it(Foob& f, T& t) { f.proc(t); }
template <class Foob, class T>
void do_it(Foob& f, const T& t) { f.proc(t); }
Some Foobs take const T&s while other Foobs take just T&s, or the same Foob may even have two procs, one for the const case, one for the non-const case, that do different things.
However, the code of do_it is the same: it's just f.proc(t). Sometimes the code is many lines, but still identical. Is there any way to write do_it as just one function? Something like this, though this would clearly not work:
template <class Foob, class MaybeConst, class T>
void do_it(Foob& f, MaybeConst T& t) { f.proc(t); }
Actually if the t argument is always an lvalue then you only need the first overload! If the lvalue has type const U, then the template parameter T is deduced as const U and the instantiated function's second parameter type will be const U&, which will bind to the const lvalue just fine.
The second overload is only needed if the argument is an rvalue. But instead of making that a special case, why not just use perfect forwarding?
// works for both lvalues and rvalues with any cv-qualification
template <class Foob, class T>
void do_it(Foob& f, T&& t) { f.proc(std::forward<T>(t)); }
I have a templated class that can take either a scalar, or an indexed type, and I would like to call a different version of a function depending on what type it is:
template <typename Ta, typename ... T>
struct MyClass {
Ta tt; //can be either a scalar (double, complex<double>, int, etc),
//or an indexed type (MyClass, std::vector<double>, double[], etc)
//....
//I would like the following to be called when tt is a scalar type:
auto operator[]( size_t i ) { return tt; };
//I would like the following to be called when tt has [] overloaded:
auto operator[]( size_t i ) { return tt[i]; };
};
Is there a way to do this? Return value SFINAE doesn't work (because there isn't a template parameter on this function), Class based SFINAE doesn't seem to work (because the variadic template makes having a dummy template parameter at the end not work). Any other ideas?
I believe Xeo misunderstood what Andrew meant by "scalar". Xeo is following the C/C++ definition of scalar (as per C++11, 3.9/9) and Andrew meant something closer to the linear algebra sense (an element of the underlying field of a vector space). For instance, while for Andrew std::complex<double> is scalar, this is not the case for Xeo since he uses std::is_scalar<std::complex<double>> to check that and, of course, he gets false.
I believe, what Andrew wants is operator[](size_t i) to return:
tt[i] if tt[i] is legal; or
tt if tt[i] is illegal.
We can easily SFINAE away the first candidate above. By implementing a trait that checks whether calling tt[i] is legal or not, we can also SFINAE away the second candidate when the expression is legal. Creating this trait isn't very straightforward and relies on overload resolution (reall the classic no f(...)). I'll skip the creation of the trait and, instead, use the same ideas directly inside MyClass. To avoid polluting operator[]()'s signature with a dummy argument (needed for the overload resolution trick) I'll create private methods called value() which take the dummy argument. Then operator[]() will just delegate the call to one of them.
The code follows.
#include <type_traits> // for std::declval
template <typename Ta>
class MyClass {
// This overload is SFINAEd away when tt[i] is illegal
template <typename T = Ta, typename R = decltype(std::declval<T>()[0])>
R
value(size_t i, int) { return tt[i]; }
// This one is always present but is a worse match than the other
// one when resolving value(i, 0).
const Ta&
value(size_t, ...) { return tt; }
public:
Ta tt;
auto operator[]( size_t i ) -> decltype(this->value(0, 0)) {
return value(i, 0);
}
};
Just give the operator[]s a dummy template parameter.
template<class U = Ta, EnableIf<std::is_scalar<U>>...>
auto operator[]( size_t i ){ return tt; };
template<class U = Ta, DisableIf<std::is_scalar<U>>...>
auto operator[]( size_t i ) { return tt[i]; };
See here for some explanation about this particular style of SFINAE.