In the following code, I wanted to achieve the effect of binary operator chaining for the specific type definition I want to use - for trivial operator chaining, that a binary operator returns the same type of object, most of cases just simply returning *this, which could trivially be used again to chain the next object of the same type.
However, in my case, the binary operators takes two reference_wrappers of two same typed objects (awaitable<T>::ref) as input, and returns an aggregated object of type (awaitable<awaitable<T>::ref>), and I wanted to use the returned aggregate object to chain the next awaitable<T>::ref and again return the further aggregated object of type awaitable<awaitable<T>::ref> - notice that the returned object is always the same type of awaitable<awaitable<T>::ref>, no matter how many more chaining happens.
The friend operator with template template parameter defined at location (XXX) hoped to serve this purpose, but compilers don't seem to be willing to perform the bind.
Could anyone shed some lights on how I can achieve the result as described?
Thanks!
#include <functional>
template <typename T>
struct awaitable
{
typedef std::reference_wrapper<awaitable> ref;
// (A) - okay
friend awaitable<ref> operator||(ref a1, ref a2)
{
awaitable<ref> r;
return r;
}
// (XXX) - this doesn't bind
template < template <typename> class _awaitable >
friend awaitable<ref> operator||(typename awaitable<typename _awaitable<T>::ref>::ref a1, ref a2)
{
awaitable<ref> r;
return r;
}
};
int main(int argc, const char * argv[])
{
awaitable<void> a1;
awaitable<void> a2;
auto r1 = a1 || a2; // Okay - r1 is of type awaitable<awaitable<void>::ref>
awaitable<void> a3;
auto r3 = r1 || a3; // doesn't bind to the operator defined at XXX
return 0;
}
[EDIT] -
Answers in this post and this seem to explain the situation pretty nicely, but in my case, the friend operator has a template template parameter (which is needed to avoid recursive template instantiation), which might prevent the compilers to generate the correct namespace scope function when the template is instantiated?
It seems that the friend function with template template parameter makes template deduction fail. The solution is to remove the template template parameter, and expand the ::ref usage to std::reference_wrapper within the friend function definition:
#include <functional>
template <typename T>
struct awaitable
{
typedef std::reference_wrapper<awaitable> ref;
// (A) - okay
friend awaitable<ref> operator||(ref a1, ref a2)
{
awaitable<ref> r;
return r;
}
// (XXX) - removing the template template parameter makes the template instantiation for a specific type T to generate a namespace version of the function!
friend awaitable<ref> operator||(std::reference_wrapper<awaitable<std::reference_wrapper<awaitable<T>>>> a1, ref a2)
{
awaitable<ref> r;
return r;
}
};
// template <typename T>
// awaitable<typename awaitable<T>::ref> operator||(typename awaitable<typename awaitable<T>::ref>::ref a1, typename awaitable<T>::ref a2)
// {
// awaitable<typename awaitable<T>::ref> r;
// return r;
// }
int main(int argc, const char * argv[])
{
awaitable<void> a1;
awaitable<void> a2;
auto r1 = a1 || a2; // Okay - r1 is of type awaitable<awaitable<void>::ref>
awaitable<void> a3;
auto r3 = r1 || a3; // now it works!
return 0;
}
Demo here
is this what you need?
template < template <typename> class _awaitable, typename U >
friend auto operator||(_awaitable<std::reference_wrapper<U>> a1, ref a2)
{
awaitable<ref> r;
return r;
}
live demo
EDIT1
I saw your answer where you removed the template parameter to get it working. That works great if void is the only the type you use. If you try to use another type it will fail though. The closest I've got to get around it is explicity using std::ref(r1) e.g.
template<typename U>
friend awaitable<ref> operator||(std::reference_wrapper<awaitable<std::reference_wrapper<awaitable<U>>>> a1, ref a2)
{
std::cout << "(XXX2)" << std::endl;
awaitable<ref> r;
return r;
}
awaitable<int> a4;
auto r4 = std::ref(r1) || a4;
live demo 2
Related
For example:
std::unique_ptr<Box[]> Boxes(new Box[5]);
std::function<int(doube, double)> funcobj;
But for any variable x, decltype(x) cannot be T[] or call signature R(A1,...)
Thank you all for your helpful discussion.
Now I understand that both are truly types. You can use them in typedef declaration. But they are special you cannot use them as normal types. Here is an example to use these types.
typedef int function_type(int, int);
typedef char char_array[];
// char_array arr; // Not allowed
char_array str = "hello";
function_type add; // like function declaration;
// function add defined here.
int add(int a, int b) {
return a+b;
};
I am trying to use recursion to solve this problem where if i call
decimal<0,0,1>();
i should get the decimal number (4 in this case).
I am trying to use recursion with variadic templates but cannot get it to work.
Here's my code;
template<>
int decimal(){
return 0;
}
template<bool a,bool...pack>
int decimal(){
cout<<a<<"called"<<endl;
return a*2 + decimal<pack...>();
};
int main(int argc, char *argv[]){
cout<<decimal<0,0,1>()<<endl;
return 0;
}
What would be the best way to solve this?
template<typename = void>
int decimal(){
return 0;
}
template<bool a,bool...pack>
int decimal(){
cout<<a<<"called"<<endl;
return a + 2*decimal<pack...>();
};
The problem was with the recursive case, where it expects to be able to call decltype<>(). That is what I have defined in the first overload above. You can essentially ignore the typename=void, the is just necessary to allow the first one to compile.
A possible solution can be the use of a constexpr function (so you can use it's values it's value run-time, when appropriate) where the values are argument of the function.
Something like
#include <iostream>
constexpr int decimal ()
{ return 0; }
template <typename T, typename ... packT>
constexpr int decimal (T const & a, packT ... pack)
{ return a*2 + decimal(pack...); }
int main(int argc, char *argv[])
{
constexpr int val { decimal(0, 0, 1) };
static_assert( val == 2, "!");
std::cout << val << std::endl;
return 0;
}
But I obtain 2, not 4.
Are you sure that your code should return 4?
-- EDIT --
As pointed by aschepler, my example decimal() template function return "eturns twice the sum of its arguments, which is not" what do you want.
Well, with 0, 1, true and false you obtain the same; with other number, you obtain different results.
But you can modify decimal() as follows
template <typename ... packT>
constexpr int decimal (bool a, packT ... pack)
{ return a*2 + decimal(pack...); }
to avoid this problem.
This is a C++14 solution. It is mostly C++11, except for std::integral_sequence nad std::index_sequence, both of which are relatively easy to implement in C++11.
template<bool...bs>
using bools = std::integer_sequence<bool, bs...>;
template<std::uint64_t x>
using uint64 = std::integral_constant< std::uint64_t, x >;
template<std::size_t N>
constexpr uint64< ((std::uint64_t)1) << (std::uint64_t)N > bit{};
template<std::uint64_t... xs>
struct or_bits : uint64<0> {};
template<std::int64_t x0, std::int64_t... xs>
struct or_bits<x0, xs...> : uint64<x0 | or_bits<xs...>{} > {};
template<bool...bs, std::size_t...Is>
constexpr
uint64<
or_bits<
uint64<
bs?bit<Is>:std::uint64_t(0)
>{}...
>{}
>
from_binary( bools<bs...> bits, std::index_sequence<Is...> ) {
(void)bits; // suppress warning
return {};
}
template<bool...bs>
constexpr
auto from_binary( bools<bs...> bits={} )
-> decltype( from_binary( bits, std::make_index_sequence<sizeof...(bs)>{} ) )
{ return {}; }
It generates the resulting value as a type with a constexpr conversion to scalar. This is slightly more powerful than a constexpr function in its "compile-time-ness".
It assumes that the first bit is the most significant bit in the list.
You can use from_binary<1,0,1>() or from_binary( bools<1,0,1>{} ).
Live example.
This particular style of type-based programming results in code that does all of its work in its signature. The bodies consist of return {};.
Is it possible to loop over sub range using range based for loop ?
std::vector <std::string> inputs={"1","abaaaa","abc","cda"};
for (auto &it : new_vector(inputs.begin()+1, inputs.end()))
{
// …
}
You could use Boost's iterator_range:
for (auto &it : boost::make_iterator_range(inputs.begin()+1, inputs.end()))
{
cout << it << endl;
}
demo
Alternatively you could write your own wrapper.
Unfortunately, there is no such thing in the C++ standard library. However, you can define your own wrapper like this (requires at least C++ 11 - which should not be problem in 2021):
template<typename Iter>
struct range
{
Iter b, e;
Iter begin() const { return b; }
Iter end() const { return e; }
};
template<typename T>
auto slice(const T& c, std::size_t from, std::size_t to = -1) -> range<decltype(c.begin())>
{
to = (to > c.size() ? c.size() : to);
return range<decltype(c.begin())>{c.begin() + from, c.begin() + to};
}
And then you can use it:
std::vector<int> items(100);
// Iterates from 4th to 49th item
for (auto x: slice(items, 4, 50))
{
}
// Iterates from 15th to the last item
for (auto x: slice(items, 15))
{
}
tl;dr
Long story short, you #include <range/v3/view/subrange.hpp> and change your new_vector to ranges::subrange. And that's it. Demo on Compiler Explorer.
So
Given the name you imagine for this function, new_vector, maybe you think you need the entity on the right of : to be a std::vector or at least some type of container.
If that's the case, then change your mind, it's not needed. All that : wants from its "right hand side" is that it have begin and end defined on them, member or non member. For instance, this compiles and runs just fine:
struct A {};
int* begin(A);
int* end(A);
struct B {
int* begin();
int* end();
};
int main()
{
for (auto it : A{}) {}
for (auto it : B{}) {}
}
I have re-arranged an example regarding the std::forward with template.
I have used a wrapper function and everything is fine, if i declare it as void function. It works as expected.
#include<iostream>
using namespace std;
template <typename T, typename U>
auto summation(T const &a, U const& b) -> decltype(T{}, U{}) {
cout << "call by lvalue" << endl;
return a+b;
}
template <typename T, typename U>
auto summation(T&& a, U && b) -> decltype(T{},U{}) {
cout << "call by rvalue" << endl;
return a+b;
}
template<typename T,typename U> void func(T&& a, U && b) {
summation(forward<T>(a), forward<U>(b));
}
int main() {
int x = 10;
double y = 20;
func(x,y);
func(10,20);
}
but if I want to return a type from a wrapper function, no matter what I used, I got error on lvalues function call ONLY, fund(x,y), stating "....function does not match the arguments"... the other fund(10,20) works.
template<typename T,typename U> auto func(T&& a, U && b) -> decltype(T{}, U{}) {
return summation(forward<T>(a), forward<U>(b));
}
and even using c++14 decltype(auto) for deducing the return type of forwarding functions and similar wrappers
template<typename T,typename U> decltype(auto) func(T&& a, U && b) {
return summation(forward<T>(a), forward<U>(b));
}
it does not work either, stating "decline(type) is C++o1 extension..." that, thank you compiler, but it does help.
One non sense horrible solution is declare the return type or T or U as return type. This compiles even if I got a warning stating "Reference to stack memory associated to local variable returned"
template<typename T,typename U> U func(T&& a, U && b) {
auto res = summation(forward<T>(a), forward<U>(b));
return res;
}
the return type of std::forward given (t) the object to be forwarded is
static_cast<T&&>(t)
therefore it the first solution with auto should work but it does not.
Any suggestion on this ?
Thanks for any help
decltype means the type of the expression given in its argument. So
decltype(T {}, U {})
will be the type of the expression T{}, U{}. You have the comma operator here, and so the type of the expression is the type of the expression after the comma, which is U{}, hence decltype (T{}, U{}) gives you type U (more precisely, U &&, I guess, since it is an rvalue).
What you want is
decltype(T{} + U{})
or
decltype(a+b)
(thanks to Jonathan Wakely, see comments).
Please explain how auto type deduction works when used with move semantic:
#include <iostream>
template <typename T>
struct A {
static void type() { std::cout << __PRETTY_FUNCTION__ << std::endl; }
};
float& bar() {
static float t = 5.5;
return t;
}
int foo() {
return 5;
}
int main() {
auto &&a1 = foo(); // I expected auto -> int (wrong)
auto &&a2 = bar(); // I expected auto -> float& (correct)
A<decltype(a1)>::type();
A<decltype(a2)>::type();
}
The output is:
static void A<T>::type() [with T = int&&]
static void A<T>::type() [with T = float&]
auto&& (just like T&& in parameter of a function template where T is a template parameter of that function template) follows slightly different rules than other deductions - it's unofficially called a "universal reference."
The idea is that if the initialiser is an lvalue of type X, the auto is deduced to X&. If it's an rvalue of type X, the auto is deduced to X. In both cases, && is then applied normally. From reference collapsing rules, X& && becomes X&, while X && remains X&&.
This means that in your a1 case, auto is indeed deduced to int, but a1 is then naturally declared with type int&&, and that's what decltype(a1) gives you.
At the same time, the auto in a2 is float&, and so is the type of a2, which the decltype(a2) again confirms.
In other words, your expectation that auto -> int in the first case is correct, but the type of a1 is auto &&a1, not just auto a1.
auto &&a1 = foo();
Return type of foo() is int. Since you declared a1 as auto&&, it expands to int&&, and that is what you get for type of a1.
auto &&a2 = bar();
Return type of bar() is float&. Since you declared a2 as auto&&, it expands to float& && and that converts to float& following the rules.
This answer explains the rules how the universal reference expands :
&& -> &&
&& & -> &
& && -> &
&& && -> &&