This example code below is designed to express the need to achieve 2 goals on multiple instances of other code:
bar
.bar
.As the number of parameters in the function bar
grows, it becomes more difficult to define every permutation of l-value and r-value references.
Currently, I am not able to use r-value references in bar
function.
Is there any method, that I'm not aware of? Or should I write every permutation as a last resort?
#include <iostream>
#include <vector>
// Skip down to the bar function
template<typename DT> struct X
{
DT data;
X(DT data) : data(data * 2) {}
template<typename VT>
float foo(VT value) { return data + value; }
};
template<typename DT> struct Y
{
DT data;
Y(DT data) : data(data * 4) {}
template<typename VT>
float foo(VT value) { return value * data; }
};
template<typename DT> struct Z
{
DT data;
Z(DT data) : data(data * 8) {}
template<typename VT>
float foo(VT value) { return (value + 1) * (data + 1); }
};
// Function of interest
template <template<typename> typename XT, typename AT,
template<typename> typename YT, typename BT,
template<typename> typename ZT, typename CT>
float bar(XT<AT>&& a, YT<BT>&& b, ZT<CT>&& c) // Universal references are not allowed
{
XT<float> a_result(b.data - c.data);
YT<float> b_result(a.data - c.data);
XT<float> c_result(a.data + b.data);
return a_result.foo(a.data) + b_result.foo(b.data) + c_result.foo(c.data);
}
int main()
{
X<float> x(1.);
Y<double> y(2.);
Z<int> z(3);
double result = bar(x, y, z);
std::cout << result << std::endl;
}
The Godbolt output for the code above
Could not execute the program
Compiler returned: 1
Compiler stderr
<source>: In function 'int main()':
<source>:52:23: error: cannot bind rvalue reference of type 'X<float>&&' to lvalue of type 'X<float>'
52 | double result = bar(x, y, z);
| ^
<source>:39:20: note: initializing argument 1 of 'float bar(XT<AT>&&, YT<BT>&&, ZT<CT>&&) [with XT = X; AT = float; YT = Y; BT = double; ZT = Z; CT = int]'
39 | float bar(XT<AT>&& a, YT<BT>&& b, ZT<CT>&& c) { // Universal references not allowed
| ~~~~~~~~~^
In your shown simplified code, it seems like you need only const
qualified l-value reference.
However, if you insist having the forwarding ref (àka. universal reference) been used instead, I recommend changing the template template parameters to be a simple template function as follows:
template <typename XT, typename YT, typename ZT>
auto bar(XT&& a, YT&& b, ZT&& c)
// uncomment the following if you want to restrict
// the rvalue references parameters been used (aka. SFINAE)
// -> std::enable_if_t<
// !std::is_rvalue_reference_v<decltype(a)> &&
// !std::is_rvalue_reference_v<decltype(b)> &&
// !std::is_rvalue_reference_v<decltype(c)>
// , float>
{
// type alias for common type
using CommonType = std::common_type_t<decltype(a.data), decltype(b.data), decltype(c.data)>;
X<CommonType> a_result(b.data - c.data);
Y<CommonType> b_result(a.data - c.data);
Z<CommonType> c_result(a.data + b.data);
return static_cast<float>(a_result.foo(a.data) + b_result.foo(b.data) + c_result.foo(c.data));
}