Writing a general minimum function, Two questions came to my mind. The code works fine with any input type and different argument number:
namespace xyz
{
template <typename T1, typename T2>
auto min(const T1 &a, const T2 &b) -> decltype(a+b)
{
return a < b ? a : b;
}
template <typename T1, typename T2, typename ... Args>
auto min(const T1 &a, const T2 &b, Args ... args) -> decltype(a+b)
{
return min(min(a, b), args...);
}
}
int main()
{
cout << xyz::min(4, 5.8f, 3, 1.8, 3, 1.1, 9) << endl;
// ^ ^ ^
// | | |
// float double int
}
Is there a better replacement for decltype(a+b)? I thing there's a standard class which I can't remember, something like decltype(std::THE_RESULT<a,b>::type).
The returned type of that decltype(std::THE_RESULT<a,b>::type)is const & or not ?
std::common_type(c++11):
For non-specialized std::common_type, the rules for determining the
common type between every pair T1, T2 are exactly the rules for
determining the return type of the ternary conditional operator where
T1 and T2 are the types of its second and the third operands.
and
For arithmetic types, the common type may also be viewed as the type
of the (possibly mixed-mode) arithmetic expression such as T0() + T1()
+ ... + Tn().
Not sure about const&, but you could play with std::remove_cv and std::remove_reference (and std::is_reference to find out the answer).
In fact, here's a list of type support utilities. Knock yourself out.
After the answer and worth comments I did it as below:
template <typename T1, typename T2>
auto min(const T1 &a, const T2 &b)
-> typename std::common_type<const T1&, const T2&>::type
{
return a < b ? a : b;
}
template <typename T1, typename T2, typename ... Args>
auto min(const T1 &a, const T2 &b, const Args& ... args)
-> typename std::common_type<const T1&, const T2&, const Args& ...>::type
{
return min(min(a, b), args...);
}
Related
I'm learning about function template specialization in C++ and am tasked with writing a template function called plus that returns the sum of it's two arguments which maybe of different types. One version that accepts by value and another by pointer. As an added challenge, I'm asked to overload this function so that it concatenates two strings.
template <typename T1, typename T2> decltype(auto) plus(const T1& a, const T2& b) {
return a + b;
}
template <typename T1, typename T2> decltype(auto) plus(const T1* a, const T2* b) {
return *a + *b;
}
// concatenate two strings
template <>
std::string_view plus<std::string_view, std::string_view> (std::string_view a, std::string_view b) {
return std::string { a } + std::string{ b };
}
The problem is that I'm getting an error on the specialization overload of the function to concatenate two strings. The reason I decided to choose std::string_view over std::string is so that when calling the function with string literals (const char*) it wouldn't resolve to the second definition that accepts a const * which I'm guessing would be resolved over std::string.
So I can't really figure out what's going on. Just a wild guess but maybe this has something to do with me having two different template functions called plus and it can't figure out which one I'm trying to specialize / overload?
UPDATE:
The issue seems to be with template resolution. The definition that accepts const T* is always preferred for any string literals. Just trying to find a fix.
This would be my suggestion:
template <typename T1, typename T2, typename T3> T3 plus(const T1& a, const T2& b) {
return a + b;
}
template <typename T1, typename T2, typename T3> T3 plus(const T1* a, const T2* b) {
return *a + *b;
}
template <typename T1, typename T2, typename T3> T3 plus(T1 a, T2 b) {
return a + b;
}
// concatenate two strings
template <>
std::string plus<std::string_view, std::string_view> (std::string_view a, std::string_view b) {
return std::string(a).append(b);
}
Since string views needs to refer to the content of another string you need to return a string since the newly created string_view would point to a temporary object.
Also there is no way to concatenate 2 string_view's together since concatenating two strings together would require that string_view's are able to hold references to other string views (since they don't hold string content themselves).
Furthermore, a third typename is required since this implementation would return another type (std::string) since you don't want to return a string_view of a temporary
If you have access to C++20, then this could be done pretty easily with concepts.
Anything that's convertible to string_view, such as string and const char*, will be taken to this function:
template<typename T>
concept StringView = std::convertible_to<T, std::string_view>;
auto plus(StringView auto a, StringView auto b)
{
return std::string(a).append(b);
}
Similarly, you can define other concepts easily and just exlucde StringView:
template<typename T>
concept Reference = std::is_reference_v<T> && !StringView<T>;
template<typename T>
concept Pointer = std::is_pointer_v<T> && !StringView<T>;
auto plus(const Reference auto a, const Reference auto b)
{
⋮
⋮
I have a concept for normal binary operators
template<typename Op, typename T> concept is_binary_operation =
requires (const T& t1, const T& t2) // e.g. a+b
{
{Op()(t1,t2)}->std::convertible_to<T>;
};
and a concept for compound assignment operators
template<typename Op, typename T> concept is_operation_and_assign =
requires (T& t1, const T& t2) // e.g a += b;
{
{Op()(t1,t2)}->std::convertible_to<T>;
};
For compound assignment operators this works as expected:
template<typename T> struct op_and_assign
{
T& operator()(T& t1, const T& t2)
{
t1 += t2;
return t1;
}
};
This "is_operation_and_assign" but not "is_binary_operation"
std::cout << is_binary_operation<op_and_assign<double>, double> << " ";
std::cout << is_operation_and_assign<op_and_assign<double>, double> << std::endl;
prints "0 1". std::plus, however, satisfies both concepts:
std::cout << is_binary_operation<std::plus<double>, double> << " ";
std::cout << is_operation_and_assign<std::plus<double>, double> << std::endl;
prints "1 1".
How do I have to change the concept "is_operation_and_assign" so that I get the output "1 0", i.e. so that it will fulfilled by op_and_assign but not by std::plus?
To make more clear what I need: I have two versions of an algorithm, one using the compound assignment operator, one using the binary operator:
template<typename Op, typename T>
int f() requires is_operation_and_assign<Op, T>
{
return 0;
}
template<typename Op, typename T>
int f() requires is_binary_operation<Op, T>
{
return 1;
}
I can call the version for op_and_assign
f<op_and_assign<double>, double>();
but the version for std::plus
f<std::plus<double>, double>();
does not compile. (error: call to 'f' is ambiguous)
Update: in the meanwhile I found a workaround:
When I simply add && !is_binary_operation<Op, T> to the first f:
template<typename Op, typename T>
int f() requires (is_operation_and_assign<Op, T>
&& !is_binary_operation<Op, T>)
{
return 0;
}
template<typename Op, typename T>
int f() requires is_binary_operation<Op, T>
{
return 1;
}
then the second call is no longer ambiguous, i.e. both
f<op_and_assign<double>, double>();
f<std::plus<double>, double>();
compile (and choose the desired function).
It's important to clarify what actually your concept is checking, because it's not what you think it is.
This:
template<typename Op, typename T> concept is_operation_and_assign =
requires (T& t1, const T& t2) // e.g a += b;
{
{Op()(t1,t2)}->std::convertible_to<T>;
};
Checks that you can invoke Op()(t1, t2) with a T& and a T const& and that you get something that satisfies convertible_to<T>. When you provide:
template<typename T> struct op_and_assign
{
T& operator()(T& t1, const T& t2)
{
t1 += t2;
return t1;
}
};
as the first template parameter, what does that actually check? This is an unevaluated expression, we're checking to see if we can invoke op_and_assign<T>(). We're not evaluating the body of the call operator, we're just checking to see if it's a valid call. So it's no different than if we wrote:
template<typename T> struct op_and_assign
{
T& operator()(T& t1, const T& t2);
};
It's unevaluated, there's no body, so the only things that matter are constraints. Here, there are no constraints, so op_and_assign is always invokable as long as the arguments are convertible.
When you do this:
is_binary_operation<op_and_assign<double>, double>
You're effectively asking if you can convert the arguments appropriately. For is_binary_operation, you're providing two arguments of type double const& (from your requires expression) but op_and_assign<double> needs to take one double&. That's why this particular check doesn't work.
For how to fix it. op_and_assign which should probably look something like this:
struct op_and_assign
{
template <typename T, typename U>
auto operator()(T&& t, U&& u) const -> decltype(t += u);
};
Now we're actually checking if we can perform +=.
But that won't change that you can't assign to a double const&. You're getting the correct answer there, even if you weren't doing the check you intended to.
I am writing some template meta programming code. For some reasons, I want to make every object in my code has different type. The original code looks like this:
template<unsigned int index>
class Class1{
};
template<typename T1, typename T2, unsigned int index>
class Class2{
std::tuple<T1*, T2*> v;
public:
Class2(T1* t1, T2* t2): v(std::tuple<T1*, T2*>(t1, t2)) {}
};
template<unsigned int index>
auto makeClass1() {
return Class1<index>();
}
template<unsigned int index, typename T1, typename T2>
auto mul(T1& t1, T2& t2) {
return Class2<T1, T2, index>(&t1, &t2);
}
int main() {
auto t1 = makeClass1<0>(); // Type of TT1 is Class1<0>
auto t2 = makeClass1<1>(); // Type of TT2 is Class1<1>
auto m1 = mul<0>(t1, t2);
auto m2 = mul<1>(t1, t2); // Type of m2 is different from type of m1.
}
This code is work, but I wish my code is easy to use. So I want to ask is there any solution that can make the code look like this:
template<unsigned int index>
class Class1{
};
template<typename T1, typename T2, unsigned int index>
class Class2{
std::tuple<T1*, T2*> v;
public:
Class2(T1* t1, T2* t2): v(std::tuple<T1*, T2*>(t1, t2)) {}
};
template<unsigned int index = IncreaseCounter<?>::value>
auto makeClass1() {
return Class1<index>();
}
template<unsigned int index = IncreaseCounter<?>::value, typename T1, typename T2>
auto operator*(T1& t1, T2& t2) {
return Class2<T1, T2, index>(&t1, &t2);
}
int main() {
auto t1 = makeClass1(); // Type of TT1 is Class1<0>
auto t2 = makeClass1(); // Type of TT2 is Class1<1>
auto m1 = t1*t2
auto m2 = t1*t2; // Type of m2 is different from type of m1.
}
Note: I think I need a compile-time counter. But except the macro solution:__COUNTER__ and __LINE__ , I can't find any other compile-time solution. The macro solution is ineffective to my code.
Except the compile-time counter, any other solution is ok.
Thank you for reading my question. Due to my poor english expression ability,please bear with me for the wrong sentences.
In C++20, you might do:
template <typename = decltype([]{})>
class Class1{};
template<typename T1, typename T2, typename = decltype([]{})>
class Class2{
std::tuple<T1*, T2*> v;
public:
Class2(T1* t1, T2* t2): v(std::tuple<T1*, T2*>(t1, t2)) {}
};
template <typename T = decltype([]{})>
auto makeClass1() { return Class1<T>();}
template<typename T1, typename T2, typename T = decltype([]{})>
auto operator*(T1& t1, T2& t2) {
return Class2<T1, T2, T>(&t1, &t2);
}
int main() {
auto t1 = makeClass1();
auto t2 = makeClass1(); // Type of t2 is different from type of t1.
auto m1 = t1*t2;
auto m2 = t1*t2; // Type of m2 is different from type of m1.
static_assert(!std::is_same_v<decltype(t1), decltype(t2)>);
static_assert(!std::is_same_v<decltype(m1), decltype(m2)>);
}
Demo.
Let's regard the core of your question:
auto m1 = t1*t2;
auto m2 = t1*t2; // Type of m2 is different from type of m1.
You have exactly the same expression (t1*t2) but you wish this expression to produce two different types!
Overall your idea of counting objects in compile time is loose. What would you expect in this case?
for (int i = 0; i < 10; ++i) {
auto m = t1*t2;
}
Returning back to your code: you somehow need to introduce this compile-time index, like you do in mul<index>: either in mul or in the result type (explicit type instead of auto + appropriate conversion).
I intend to implement a template function which would accept two functions and their parameters list and then created two wrappers. I have already implemented similar solution to accept two function and create wrapper using std::bind() but using hard-coded parameters. The solution is something like this:
template <typename T1, typename T2, typename T3, typename T4>
myWrapper (T2 func1, T4 func2) {
std::function<T1>ff = std::bind(func1, 1, placeholders::_1);
std::function<T3>ff = std::bind(func1, 110, 20, "something");
}
As you see the parameters to std::bind() are hard-coded in both cases which I would like to read them through myWrapper() function. Is there any way to read two variadic templates (list of variables of arbitrary length and type) and send them to two std::bind() calls I have in the above code?
Thanks,
Dan
Not sure to understand what you want but I suppose you need to use std::tuple (or something similar) to separate the two sequences of args.
The following is an full working example of what I mean, according what I've understand of what do you want.
#include <tuple>
#include <string>
#include <utility>
#include <iostream>
#include <functional>
template <typename T1, typename T2, typename T3, typename T4,
typename ... Ts1, typename ... Ts2,
std::size_t ... Is1, std::size_t ... Is2>
auto myWrapperH (T3 func1, T4 func2, std::tuple<Ts1...> const & tp1,
std::tuple<Ts2...> const & tp2,
std::index_sequence<Is1...> const &,
std::index_sequence<Is2...> const &)
{
T1 f1 = std::bind(func1, std::get<Is1>(tp1)...);
T2 f2 = std::bind(func2, std::get<Is2>(tp2)...);
return std::make_pair(f1, f2);
}
template <typename T1, typename T2, typename T3, typename T4,
typename ... Ts1, typename ... Ts2>
auto myWrapper (T3 func1, T4 func2, std::tuple<Ts1...> const & tp1,
std::tuple<Ts2...> const & tp2)
{ return myWrapperH<T1, T2>(func1, func2, tp1, tp2,
std::make_index_sequence<sizeof...(Ts1)>{},
std::make_index_sequence<sizeof...(Ts2)>{}); }
int foo (int a, int b)
{ return a+b; }
std::size_t bar (int a, int b, std::string const & str)
{ return str.size() + a + b; }
int main ()
{
using fType1 = std::function<int(int)>;
using fType2 = std::function<long()>;
auto mwr = myWrapper<fType1, fType2>(&foo, &bar,
std::make_tuple(1, std::placeholders::_1),
std::make_tuple(110, 20, std::string{"something"}));
std::cout << mwr.first(5) << std::endl; // print 6
std::cout << mwr.second() << std::endl; // print 139
}
Unfortunately is a C++14 code (auto return type; std::index_sequence and std::make_index_sequence) but should be easy to adapt in C++11.
-- EDIT --
As pointed by Banan (thanks!) there is no need to explicit the type of returned functions (T1, T2).
Using the auto return type the example can be simplified as follows
#include <tuple>
#include <string>
#include <utility>
#include <iostream>
#include <functional>
template <typename F1, typename F2, typename ... Ts1, typename ... Ts2,
std::size_t ... Is1, std::size_t ... Is2>
auto myWrapperH (F1 func1, F2 func2, std::tuple<Ts1...> const & tp1,
std::tuple<Ts2...> const & tp2,
std::index_sequence<Is1...> const &,
std::index_sequence<Is2...> const &)
{ return std::make_pair(std::bind(func1, std::get<Is1>(tp1)...),
std::bind(func2, std::get<Is2>(tp2)...)); }
template <typename F1, typename F2, typename ... Ts1, typename ... Ts2>
auto myWrapper (F1 func1, F2 func2, std::tuple<Ts1...> const & tp1,
std::tuple<Ts2...> const & tp2)
{ return myWrapperH(func1, func2, tp1, tp2,
std::make_index_sequence<sizeof...(Ts1)>{},
std::make_index_sequence<sizeof...(Ts2)>{}); }
int foo (int a, int b)
{ return a+b; }
std::size_t bar (int a, int b, std::string const & str)
{ return str.size() + a + b; }
int main ()
{
auto mwr = myWrapper(&foo, &bar,
std::make_tuple(1, std::placeholders::_1),
std::make_tuple(110, 20, std::string{"something"}));
std::cout << mwr.first(5) << std::endl; // print 6
std::cout << mwr.second() << std::endl; // print 139
}
Regarding your idea of a variadicStruct you may wish to have a look at std::tuple.
template<class... Ts, class... Us>
void do_stuff(const std::tuple<Ts...>&, const std::tuple<Us...>&) {
std::cout << "two variadic packs with " << sizeof...(Ts)
<< " and " << sizeof...(Us) << " elements." << std::endl;
}
To be called like this:
do_stuff(std::make_tuple(4.7, 'x', 1.0, 4, 8l), std::make_tuple("foo", 1));
You can use std::make_tuple and std::apply, for example:
template <typename R, typename Func1, typename Args1>
void wrapper (Func1 f1, Args1 a1)
{
std::function<R> wrapped1 = [f1, a1]{ return std::apply(f1, a1); };
}
// ...
auto deg_to_rad = [](int x){ return x / 180.f * 3.1415f; };
wrapper<float>(deg_to_rad, std::make_tuple(45));
std::apply requires C++17 support. However, the linked page on cppreference contains a possible implementation that works in C++11.
Why isn't it possible to compare two tuples of different size like this:
#include <tuple>
int main() {
std::tuple<int, int> t1(1, 2);
std::tuple<int> t2(1);
if(std::tuple_size<decltype(t1)>::value == std::tuple_size<decltype(t2)>::value)
return (t1 == t2);
else
return 0;
}
I know that t1==t2 is not possible. But in this example it wouldn't be executed. Is there a possibility to compare tuples of different sizes?
operator== requires the tuples to be of equal lengths.
§ 20.4.2.7 [tuple.rel]:
template<class... TTypes, class... UTypes>
constexpr bool operator==(const tuple<TTypes...>& t, const tuple<UTypes...>& u);
1 Requires: For all i, where 0 <= i and i < sizeof...(TTypes), get<i>(t) == get<i>(u) is a valid expression returning a type that is convertible to bool. sizeof...(TTypes) == sizeof...(UTypes).
If you want two tuples of different lengths to be considered unequal, you'd need to implement this logic yourself:
template <typename... Ts, typename... Us>
auto compare(const std::tuple<Ts...>& t1, const std::tuple<Us...>& t2)
-> typename std::enable_if<sizeof...(Ts) == sizeof...(Us), bool>::type
{
return t1 == t2;
}
template <typename... Ts, typename... Us>
auto compare(const std::tuple<Ts...>& t1, const std::tuple<Us...>& t2)
-> typename std::enable_if<sizeof...(Ts) != sizeof...(Us), bool>::type
{
return false;
}
DEMO
This way, the code comparing two tuples, t1 == t2, is instantiated only when the lengths of tuples match each other. In your scenario, a compiler is unable to compile your code, since there is no predefined operator== for such a case.
You may write several overloads:
template<typename ...Ts>
bool is_equal(const std::tuple<Ts...>& lhs, const std::tuple<Ts...>& rhs)
{
return lhs == rhs;
}
template<typename ...Ts, typename... Us>
bool is_equal(const std::tuple<Ts...>&, const std::tuple<Us...>&)
{
return false;
}
Live example
You have problem with size mismatch. Read this, maybe it can help you.
Implementing comparison operators via 'tuple' and 'tie', a good idea?