A requires expression typically looks like: requires ( parameter-list(optional) ) { requirement-seq }.
Is it possible to form a disjunction(||) as a requirement in the sequence without using requires constraint-expr. For example something like:
template<typename T> concept FooBarExpert =
requires(T a, T b) {
{a.foo(b)} || {a.bar(b)}; // Req 1
{ a.baz() }; // Req 2
// and onward
};
Concepts are decomposed in conjunction and disjunction of atomic-constraints through a process called constraint normalization described in temp.constr.normal.
Only:
logical and &&,
logical or ||,
parenthesized expression ()
and id-expression of the form C<A1, A2, ..., An>, where C names a concept
are decomposed. All other expressions are atomic constraints.
So a require-expression is, as a whole, an atomic constraint. In the concept TS, require-expression were decomposed but in C++20 they are not. As far as I remember, I just read all the papers of the c++ committee related to concepts, the reason was that require-expression normalization may cause a complexity explosion that could penalize compilation speed.
So:
requires(T a, T b) {
requires requires(T a, T b){a.foo(b)}
|| requires(T a, T b){a.bar(b)}; // Req 1
{ a.baz() }; // Req 2
// and onward
};
is an atomic-constraint. And
requires(T a, T b) {
{a.foo(b)}
{ a.baz() }; // Req 2
// and onward
}
|| requires(T a, T b) {
{a.bar(b)}
{ a.baz() }; // Req 2
// and onward
};
is the disjunction of two atomic constraint. (the two requires-expression)
And finaly:
( requires(T a, T b) { a.foo(b); } || requires (T a, T b) { a.bar(b); } )
&& requires(T a, T b) { a.baz(); /* and onward */};
is the conjunction of a disjunction with an atomic constraint.
Define FooBarExpert as a conjunction (or disjunction) of the appropriate requires expressions.
template<typename T> concept bool FooBarExpert =
( requires(T a, T b) {
{ a.foo(b) };
} ||
requires(T a, T b) {
{ a.bar(b) };
}
) &&
requires(T a, T b) {
{ a.baz() };
};
Related
So I've recently learned about universal references and reference collapsing.
So let's say I have two different implementations of a max function like such.
template<class T>
T&& max(T&& a, T&& b)
{
return (a < b) ? b : a;
}
template<class T>
const T& max(const T& a, const T& b)
{
return (a < b) ? b : a;
}
One version takes its arguments by const reference and other takes them by universal reference.
What confuses me about these is when should either be used. Passing by const is mainly used so you can bind temporaries to it.
In the stl there's different uses as well. std::move takes universal reference where std::max takes by const ref.
What's the situations on where either should be used??
Say if I wanted to avoid the copy when it returns or keep a reference on return. Would it makes sense to have a const and nonconst version of the function etc
The first one should probably be:
// amended first version
template<class T>
decltype(auto) max(T&& a, T&& b) {
return (a < b) ? std::forward<T>(b) : std::forward<T>(a);
}
Otherwise it wouldn't work for temporaries (the original first option fails with two temporaries).
But even now, with the new version above, it cannot work for a mix of an rvalue and an lvalue:
int i = my_max(3, 5); // ok
i = my_max(i, 15); // fails
// no known conversion from 'int' to 'int &&' for 1st argument
To add to that, the return value of the first option is either an lvalue-ref or an rvalue-ref (with or without const, depending on the arguments). Getting back rvalue-ref to a temporary would not be the best idea, it would work if we immediately copy from it, but then it would be better to return byvalue, which is not the approach taken by the first version.
The pitfall with the second, the const-lvalue-ref version, is that if a temporary is sent to it, we return a const-lvalue-ref to a temporary which is bug prone (well, not more than returning an rvalue-ref to a temporary, but still). If you copy it immediately you are fine, if you take it by-ref you are in the UB zone:
// second version
template<class T>
const T& max(const T& a, const T& b) {
return (a < b) ? b : a;
}
std::string themax1 = max("hello"s, "world"s); // ok
const std::string& themax2 = max("hello"s, "world"s); // dangling ref
To solve the above problem, with the cost of redundant copying for the lvalue-ref case, we can have another option, returning byvalue:
// third version
template<class T1, class T2>
auto max(T1&& a, T2&& b) {
return (a < b) ? std::forward<T2>(b) : std::forward<T1>(a);
}
The language itself took the 2nd option for std::max, i.e. getting and returning const-ref. And the user shall be careful enough not to take a reference to temporaries.
Another option might be to support both rvalue and lvalue in their own semantic, with two overloaded functions:
template<class T1, class T2>
auto my_max(T1&& a, T2&& b) {
return (a < b) ? std::forward<T2>(b) : std::forward<T1>(a);
}
template<class T>
const T& my_max(const T& a, const T& b) {
return (a < b) ? b : a;
}
With this approach, you can always get the result as a const-ref: if you went to the first one you get back a value and extend its lifetime, if you went to the second one you bind a const-ref to a const-ref:
int i = my_max(3, 5); // first, copying
const int& i2 = my_max(i, 25); // first, life time is extended
const std::string& s = my_max("hi"s, "hello"s); // first, life time is extended
const std::string s2 = my_max("hi"s, "hello"s); // first, copying
const std::string& s3 = my_max(s, s2); // second, actual ref, no life time extension
The problem with the above suggestion is that it only takes you to the lvalue-ref version if both arguments are const lvalue-ref. If you want to cover all cases you will have to actually cover them all, as in the code below:
// handle rvalue-refs
template<class T1, class T2>
auto my_max(T1&& a, T2&& b) {
return (a < b) ? std::forward<T2>(b) : std::forward<T1>(a);
}
// handle all lvalue-ref combinations
template<class T>
const T& my_max(const T& a, const T& b) {
return (a < b) ? b : a;
}
template<class T>
const T& my_max(T& a, const T& b) {
return (a < b) ? b : a;
}
template<class T>
const T& my_max(const T& a, T& b) {
return (a < b) ? b : a;
}
template<class T>
const T& my_max(T& a, T& b) {
return (a < b) ? b : a;
}
A last approach to achieve the overloading, and supporting all kind of lvalue-ref (const and non-const, including a mixture), without the need for implementing the 4 combinations for lvalue-ref, would be based on SFINAE (here presented with C++20 with a constraint, using requires):
template<class T1, class T2>
auto my_max(T1&& a, T2&& b) {
return (a < b) ? std::forward<T2>(b) : std::forward<T1>(a);
}
template<class T1, class T2>
requires std::is_lvalue_reference_v<T1> &&
std::is_lvalue_reference_v<T2> &&
std::is_same_v<std::remove_cvref_t<T1>, std::remove_cvref_t<T2>>
auto& my_max(T1&& a, T2&& b) {
return (a < b) ? b : a;
}
And if you bear with me for one past the last... (hope you are not in a bucket for monsieur state by now). We can achieve it all in one function!, and as a side benefit, even allow cases of comparison between derived and base if supported, so the following would work fine:
A a = 1;
B b = 2; // B is derived from A
// if comparison between A and B is supported, you can do
const A& max1 = my_max(a, b); // const-ref to b
const A& max2 = my_max(a, B{-1}); // const-ref to a temporary copied from a
const A& max3 = my_max(a, B{3}); // const-ref to B{3}
This would be the code to support this with a single function:
template<typename T1, typename T2>
struct common_return {
using type = std::common_reference_t<T1, T2>;
};
template<typename T1, typename T2>
requires std::is_lvalue_reference_v<T1> &&
std::is_lvalue_reference_v<T2> &&
has_common_base<T1, T2> // see code in link below
struct common_return<T1, T2> {
using type = const std::common_reference_t<T1, T2>&;
};
template<typename T1, typename T2>
using common_return_t = typename common_return<T1, T2>::type;
template<class T1, class T2>
common_return_t<T1, T2> my_max(T1&& a, T2&& b)
{
if(a < b) {
return std::forward<T2>(b);
}
return std::forward<T1>(a);
}
The machinery for the above can be found here.
For the variadic version of this, you can follow this SO post.
Can someone explain how char datatype is supported by AdditionFeasible concept in below code snippet and how we can prevent that and ensure that it only works for numbers ?
template< class T >
concept AdditionFeasible = requires(T& f, T& s)
{
f + s;
};
template <AdditionFeasible f>
f sum(f a, f b)
{
return a + b;
}
int main()
{
std::cout<<"sum of 2 and 3 is "<<sum(2,3)<<std::endl;
std::cout<<"sum of 'K' and 'V' is"<<sum('K','V')<<std::endl;
return 0;
}
STL has a type trailt is_arimethic to ensure the type is number only as well as avoid unexpected types which are non-numerical types. The char type is a special one we also treat it as number. The only way is to exclude it explicitly
template< class T >
concept AdditionFeasible = std::is_arithmetic_v<T> && ! std::same_as<T, char> && requires(T& f, T& s) {
f + s;
};
Basically what I want is something simple, a function:
template<typename A, typename B>
is_the_same_value(A a, B b)
{
// return true if A is the same value as B
}
This seemingly simple question is is hard as using return a == b fails for int/unsigned mix,
e.g.
is_the_same_value(-1, 0xffffffff)
would return true. clang/gcc warn about this: comparison of integers of different signs
Using something like a == b && ((a > 0) == (b > 0)) works but still triggers the compiler warning.
You can use std::common_type to remove the warning.
#include <type_traits>
template<typename A, typename B>
bool is_numbers_equal(A a, B b) {
using C = typename std::common_type<A, B>::type;
return (a > 0) == (b > 0) && static_cast<C>(a) == static_cast<C>(b);
// or maybe `... && C(a) == C(b);`
}
Well, this looks fun to optimize it for the compiler for types that both are the "same":
#include <type_traits>
template<typename A, typename B>
typename std::enable_if<
std::is_integral<A>::value &&
std::is_integral<B>::value &&
(std::is_signed<A>::value ^ std::is_signed<B>::value)
, bool>::type
is_numbers_equal(A a, B b) {
using C = typename std::common_type<A, B>::type;
return (a >= 0) == (b >= 0) &&
static_cast<C>(a) == static_cast<C>(b);
}
template<typename A, typename B>
typename std::enable_if<
! (
std::is_integral<A>::value &&
std::is_integral<B>::value &&
(std::is_signed<A>::value ^ std::is_signed<B>::value)
)
, bool>::type
is_numbers_equal(A a, B b) {
return a == b;
}
For value based comparisons where no implicit conversions (signed/unsigned etc) are allowed, checking for type equality could be an option:
#include <type_traits>
template<typename A, typename B>
bool is_the_same_value(const A& a, const B& b)
{
if constexpr(!std::is_same_v<A,B>) return false;
else return a == b;
}
Can't you use std::is_integral to check whether both types are int? Then convert them to a signed long long and then compare them.
You can compare memories:
template<typename A, typename B>
bool is_the_same_value(A a, B b)
{
return std::memcmp(&a,&b,sizeof(A)) == 0;
// return true if A is the same value as B
}
This does not look like a hard problem to me. There are enough complications so I would not call the result "simple", but it's far from complex. Just deal with the identified issues one at a time, and make sure things are symmetric with respect to the two parameters.
Issue 1: converting a negative value to an unsigned type is bad.
Issue 2: comparing different types can cause compiler warnings.
template<typename A, typename B>
bool is_the_same_value(A a, B b)
{
// Address issue 1 by returning false if exactly one of the parameters
// is negative.
if ( a < 0 && b >= 0 )
return false;
if ( b < 0 && a >= 0 )
return false;
// At this point, either both a and b are negative (hence A and B are signed types)
// or both are non-negative (hence converting to an unsigned type is fine).
// Address issue 2 by explicitly casting. Make sure you cast both
// ways to keep things symmetric.
return a == static_cast<A>(b) &&
b == static_cast<B>(a);
}
Sure, there are clever ways to make this code more compact (and less readable), but isn't the compiler supposed to be able to do those optimizations for you?
I am trying to understand new c++ features and I came up with my implementation of max function:
#include <utility>
template <typename T>
concept bool Comparable() {
return requires(T a, T b)
{
{a > b} -> bool;
{a < b} -> bool;
{a >= b} -> bool;
{a <= b} -> bool;
};
}
template<Comparable T, Comparable U>
constexpr decltype(auto) max(T&& a, U&& b)
{
return std::forward<T>(a) > std::forward<U>(b) ? std::forward<T>(a) : std::forward<U>(b);
}
int main()
{
constexpr int a = 2, b = 3;
return max(a, b);
}
Can this replace the macro version?
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
And if not what can be improved?
I am compiling with gcc-8.1.0 and -std=c++2a, -fconcepts and -O3 flags
EDIT:
Thank you for your suggestions, this is how it looks now:
#include <utility>
template<typename T, typename U>
concept bool TypesLessComparable() {
return requires(T a, U b)
{
{a < b} -> bool;
};
}
template<typename T, typename U>
constexpr decltype(auto) max(T&& a, U&& b) requires (TypesLessComparable<T, U>() == true)
{
return a < b ? std::forward<U>(b) : std::forward<T>(a);
}
int main()
{
constexpr int a = 2, b = 3;
return max(a, b);
}
std::forward<T>(a) > std::forward<U>(b) ? std::forward<T>(a) : std::forward<U>(b);
You forward the result twice. This is wrong for most types (only OK for types that implement move as a deep copy). You should remove forward from the comparison.
Is this max function decent?
No, there are at least 3 issues:
1 Unclear why you request multiple comparison operations when your function only requires one of them (it should be fine for a class only to define operator> when only max() is used)
2 Concept Comparable requires that type has comparison operations on itself, you may compare 2 object of different types. This invalidates usage of concepts (a function should use only what required/provided by concepts)
3 You may return objects of different types as well, one may convert to another but you should cover that by another concept.
Here is code example for case 2 and 3:
struct A { friend bool operator>( const A&, const A& ); };
struct B { friend bool operator>( const B&, const B& ); };
auto x = max( A(), B() );
To summarize:
Your function should require minimum set of operations on type(s), necessary for it to operate.
If type(s) passed concepts validation your function should compile successfully.
This is most probably not full set of requirements.
I want to have a function that evaluates 2 bool vars (like a truth table).
For example:
Since
T | F : T
then
myfunc('t', 'f', ||); /*defined as: bool myfunc(char lv, char rv, ????)*/
should return true;.
How can I pass the third parameter?
(I know is possible to pass it as a char* but then I will have to have another table to compare operator string and then do the operation which is something I would like to avoid)
Is it possible to pass an operator like ^ (XOR) or || (OR) or && (AND), etc to a function/method?
Declare:
template<class Func> bool myfunc(char lv, char rv, Func func);
Or if you need to link it separately:
bool myfunc(char lv, char rv, std::function<bool(bool,bool)> func);
Then you can call:
myfunc('t', 'f', std::logical_or<bool>());
#ybungalobill posted a C++ correct answer and you should stick to it. If you want to pass the operators, functions will not work, but macros would do the work:
#define MYFUNC(lv, rv, op) ....
// Call it like this
MYFUNC('t', 'f', ||);
Be careful, macros are evil.
What you can do is define proxy operators that return specific types.
namespace detail {
class or {
bool operator()(bool a, bool b) {
return a || b;
}
};
class and {
bool operator()(bool a, bool b) {
return a && b;
}
};
// etc
class X {
or operator||(X x) const { return or(); }
and operator&&(X x) const { return and(); }
};
};
const detail::X boolean;
template<typename T> bool myfunc(bool a, bool b, T t) {
return t(a, b);
}
// and/or
bool myfunc(bool a, bool b, std::function<bool (bool, bool)> func) {
return func(a, b);
}
// example
bool result = myfunc(a, b, boolean || boolean);
You can if desperate chain this effect using templates to pass complex logical expressions.
Also, the XOR operator is bitwise, not logical- although the difference is realistically nothing.
However, there's a reason that lambdas exist in C++0x and it's because this kind of thing flat out sucks in C++03.
In modern C++ can pass any operator by using lambdas.
Update 1: the proposed solution introduces small improvement which is suggested by #HolyBlackCat
#include <iostream>
template<class T, class F> void reveal_or(T a, T b, F f)
{
// using as function(a, b) instead of expression a || b is the same thing
if ( f(a, b) )
std::cout << a << " is || " << b << std::endl;
else
std::cout << a << " is not || " << b << std::endl;
}
template<class T> void reveal_or(T a, T b)
{
// reuse the already defined ||
reveal_or(a, b, [](T t1, T t2) {return t1 || t2; });
}
Don't bother how to pass parameter if || operator is defined
int main ()
{
reveal_or('1', 'a');
return 0;
}
Passing explicitly as parameter. We can pass anything, including including any exotic nonsense
int main ()
{
//same as above:
reveal_or('1', 'a', [](char t1, char t2) { return t1 || t2; });
//opposite of above
reveal_or('1', 'a', [](char t1, char t2) { return !( t1 || t2; ) });
return 0;
}
It's hard to be realized. In C++, function parameter need an memroy address to find its object, but operator is decided in compile time. Operator won't be a object. So you can think about MACRO to finish your task.