What is this header for?
It defines, just to give an example, equal_to:
namespace ranges
{
/// \addtogroup group-functional
/// #{
struct equal_to
{
template(typename T, typename U)(
/// \pre
requires equality_comparable_with<T, U>)
constexpr bool operator()(T && t, U && u) const
{
return (T &&) t == (U &&) u;
}
using is_transparent = void;
};
// ... and the others, such as !=, <, <=, ...
}
But what is the advantage of ranges::equal_to over std::equal_to? Only saving <> when instantiating an object of it's class, i.e. writing ranges::equal_to{} instead of std::equal_to<>{}?
What is the advantage of ranges::equal_to over std::equal_to? Only saving <> when instantiating an object of its class, i.e. writing ranges::equal_to{} instead of std::equal_to<>{}?
That is not true, since omitting empty <> has been allowed since C++17.
What is range/[...]/comparisons.hpp for?
The main differences between the ranges:: comparison function objects and the base versions is that the ranges versions are much more constrained, due to the requirement equality_comparable_with<T, U>. The same difference also applies between and std:: version and C++20 std::ranges:: version.
For std::equal_to{}(a, b) to be valid code, all you need is a == b being valid code.
However, for ranges::equal_to{}(a, b) to be valid code, a and b must satisfy the equality_comparable requirement. Which means not only a == b need to be valid, all equality checks between a and b must also be valid code(includes self comparison), which would include:
a == a;
a != a;
b == b;
b != b;
a == b;
a != b;
b == a;
b != a;
In the same time, there must also be a common type C between a and b, where both c == c and c != c are valid code.
Similar behaviors can be observed on other ranges:: comparison function objects, such as ranges::not_equal_to and ranges::less.
Related
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?
Maybe I missed something, but I can't find any hints: is there a constexpr ternary operator in C++17 equivalent to constexpr-if?
template<typename Mode>
class BusAddress {
public:
explicit constexpr BusAddress(Address device) :
mAddress(Mode::write ? (device.mDevice << 1) : (device.mDevice << 1) | 0x01) {}
private:
uint8_t mAddress = 0;
};
No, there is no constexepr conditional operator. But you could wrap the whole thing in a lambda and immediately evaluate it (an IIFE):
template<typename Mode>
class BusAddress {
public:
explicit constexpr BusAddress(Address device)
: mAddress([&]{
if constexpr (Mode::write) {
return device.mDevice << 1;
}
else {
return (device.mDevice << 1) | 0x01;
}
}())
{ }
private:
uint8_t mAddress = 0;
};
It may not be the sexiest code ever, but it gets the job done. Note that lambdas are constexpr by default where possible as of N4487 and P0170.
You seem to be acting under the belief that if constexpr is a performance optimization. It isn't. If you put a constant expression in a ?: clause, any compiler worth using will figure out what it resolves to and remove the condition. So the code as you have written it will almost certainly compile down to a single option, for a particular Mode.
The principle purpose of if constexpr is to eliminate the other branch entirely. That is, the compiler doesn't even check to see if it is syntactically valid. This would be for something where you if constexpr(is_default_constructible_v<T>), and if it is true, you do T(). With a regular if statement, if T isn't default constructible, T() will still have to be syntactically valid code even if the surrounding if clause is a constant expression. if constexpr removes that requirement; the compiler will discard statements that are not in the other condition.
This becomes even more complicated for ?:, because the expression's type is based on the types of the two values. As such, both expressions need to be legal expressions, even if one of them is never evaluated. A constexpr form of ?: would presumably discard the alternative that is not taken at compile time. And therefore the expression's type should really only be based on one of them.
That a very different kind of thing.
Accepted answer can also be translated into a template function for convenience:
#include <type_traits>
#include <utility>
template <bool cond_v, typename Then, typename OrElse>
decltype(auto) constexpr_if(Then&& then, OrElse&& or_else) {
if constexpr (cond_v) {
return std::forward<Then>(then);
} else {
return std::forward<OrElse>(or_else);
}
}
// examples
struct ModeFalse { static constexpr bool write = false; };
struct ModeTrue { static constexpr bool write = true; };
struct A {};
struct B {};
template <typename Mode>
auto&& test = constexpr_if<Mode::write>(A{}, B{});
static_assert(std::is_same_v<A&&, decltype(test<ModeTrue>)>);
static_assert(std::is_same_v<B&&, decltype(test<ModeFalse>)>);
const A a;
B b;
template <typename Mode>
auto&& test2 = constexpr_if<Mode::write>(a, b);
static_assert(std::is_same_v<const A&, decltype(test2<ModeTrue>)>);
static_assert(std::is_same_v<B&, decltype(test2<ModeFalse>)>);
I have a lot of code that is somehow doing exactly the same operations (inherited code), and I want to, while reworking it, compress code without losing functionality. For example let's look at the following functions:
fnc(largetype & a, largetype & b) { f(A); f(B); };
fnc(largetype && a, largetype & b) { f(A); f(B); };
fnc(largetype & a, largetype && b) { f(A); f(B); };
fnc(largetype && a, largetype && b) { f(A); f(B); };
All of them are doing exactly the same thing, but the arguments can be rvalues or lvalues without breaking the function logic. I want to allow the user to pass whatever suits the problem, but I also do not want to copy-paste all code piece by piece. I can do something like this:
fnc(largetype & a, largetype & b) { f(A); f(B); };
fnc(largetype && a, largetype & b) { fnc(a,b) };
fnc(largetype & a, largetype && b) { fnc(a,b) };
fnc(largetype && a, largetype && b) { fnc(a,b) };
which is technically correct, especially with inlining, but seems wrong to me. Is there any other, nicer way to accomplish such an effect?
The only requirements are that types passed as parameters may/will be somehow larger than default memory block size so copy avoidance is crucial. Also there is a non-zero chance that parameter can be smaller but also be a rvalue. Thread safety is optional.
I considered templating these functions, but this is in my opinion also somehow the wrong approach. Templates solve problems with different accepted types. In my case the types are the same, only passed in a different way.
As #SamVarshavchik commented, this is a usual candidate for perfect forwarding. A simple approach:
template<typename T, typename U>
void fnc(T&& a, U&& b)
{
f(std::forward<T>(a));
f(std::forward<U>(b));
}
If the user passes in objects not of type largetype, the error site will be in this function, which can be confusing to the user. To push the error site to the caller's code, we can use SFINAE to constrain the parameter types:
template<
typename T, typename U,
typename = std::enable_if_t<
std::is_same<std::decay_t<T>, largetype>{}
&& std::is_same<std::decay_t<U>, largetype>{}
>
>
void fnc(T&& a, U&& b)
{
f(std::forward<T>(a));
f(std::forward<U>(b));
}
Online Demo
Alternatively, you may want to keep the error site inside of fnc but give a much clearer error message – this can be accomplished with static_assert:
template<typename T, typename U>
void fnc(T&& a, U&& b)
{
static_assert(std::is_same<std::decay_t<T>, largetype>{},
"argument 'a' must be of type 'largetype'");
static_assert(std::is_same<std::decay_t<U>, largetype>{},
"argument 'b' must be of type 'largetype'");
f(std::forward<T>(a));
f(std::forward<U>(b));
}
Online Demo
Suppose I have 3 types, A and B and int. I want to know if some combination of instantiations of these types have the same content of bits. I know nothing about what A or B actually contain, and I don't care. I just want to know if they're the same type and have the same bit pattern.
For example:
struct A { int a{2}; };
struct B
{
B()
{
int x = 2;
b = *reinterpret_cast<double*>(&i);
}
double b;
}
template <typename T1, typename T2>
bool IsSame(const T1& t1, const T2& t2)
{
return t1 == t2; // this won't work... No '==' operator!
};
int main()
{
A a;
B b;
A a2;
a2.a = 1;
int c = 2;
IsSame(a, a); // this will be true
IsSame(a, b); // this will be false even though a and b probably have the same bit pattern.
IsSame(a, a2); // false because a2 contains a "2"
IsSame(a, c); // false because they're different types
}
How do I implement IsSame?
To check if two objects have identical representation you can write:
if ( 0 == std::memcmp(&t1, &t2, sizeof t1) )
Be aware that some of the bits of an object may not participate in its value representation; and/or there may be multiple possible representations for the same value; so objects that would compare equal via operator== may appear unequal using this method.
You should also add in a check that sizeof t1 == sizeof t2, this can be done at compile-time.
I've been writing a win32 filesystem library and I decided that rather than use TCHAR, I wanted to write a template (header only) library that would work on char/wchar_t irrespective of compiler wide/narrow options.
This left me with two problems:
My library would have to transparently/efficiently work out whether to call the ANSI/Wide version of windows functions (E.g. CreateFileA or CreateFileW) depending on how the template was expanded.
My library would sometimes need to use string literals, so I needed a way of translating a "string literal into either "" or L"" without creating a runtime cost or confusing the logic.
I've created what I think is a reasonably elegant solution to these two problems but I wanted the stack overflow community to tell me if there are any hidden costs/nasties associated with them.
Firstly (to solve 1) since my template class has T as the character type, I created the following templates/macros in a "detail" namespace within my class:
// Selector template to choose between W and A versions of win32 functions
template<typename WF, typename AF> inline WF& select_w32func(AF &, WF & pFuncW, wchar_t) { return pFuncW; }
template<typename WF, typename AF> inline AF& select_w32func(AF & pFuncA, WF &, char) { return pFuncA; }
template<typename T> inline T get_traits() { return NULL; }
#define SELECT_W32FUNC(x) auto x = detail::select_w32func(::##x##A, ::##x##W, detail::get_traits<T>());
#define SELECT_W32FUNCS(x,y) auto x = detail::select_w32func(::x, ::y, detail::get_traits<T>());
Next (to solve 2) I created the following templates and macro:
template<typename WC, typename AC> inline const WC* select_chartrait(const AC*, const WC* b, wchar_t) { return b; }
template<typename WC, typename AC> inline const AC* select_chartrait(const AC* a, const WC*, char) { return a; }
template<typename WC, typename AC> inline const WC select_chartrait(const AC, const WC b, wchar_t) { return b; }
template<typename WC, typename AC> inline const AC select_chartrait(const AC a, const WC, char) { return a; }
// Macro which allows string literals to be adapted by template parameter T
#define _S(x) detail::select_chartrait((x), (L##x), detail::get_traits<T>())
This allows me to write the constructors and member functions of my class almost as if I wasn't dealing with templated parameters, for example, the following constructor, constructs a w32file object with a CSIDL special folder identifier as it's parent:
basic_w32file(DWORD dwCSIDL, std::basic_string<T> & child) : m_name( MAX_PATH, ' ' )
{
SELECT_W32FUNC(PathCombine);
SELECT_W32FUNCS(strlen, wcslen);
basic_w32file<T> parent( getSystemDirectory ( dwCSIDL, _S(' ')));
if (child == _S(".") || child == _S(".."))
PathCombine(&m_name[0], parent.getAbsoluteName().c_str(), child.c_str());
else
PathCombine(&m_name[0], parent.getPath().c_str(), child.c_str());
m_name.resize( strlen(&m_name[0]) );
}
As you can see, the SELECT_W32FUNC macro overrides the globally namespaced PathCombine definition and replaces it with a function reference to either the A or W version of the function. _S("..") puts the correct string literal in place.
My feeling is that this will be very efficient since the template selector functions will be inlined and have essentially no runtime logic in them, meaning that they should be optimized down to almost nothing, is this a correct assumption?