In my C++ code, I wrote like this:
template <typename T, typename Pred>
inline const T BestOfTwo(const T& lhs, const T& rhs, Pred p = std::less<T>())
{
return p(lhs, rhs) ? lhs : rhs;
}
But this didn't work when I called BestOfTwo(3, 5). The compiler told me that no instance of overload matched. So now I have to write it like this:
template <typename T, typename Pred = std::less<T> >
inline const T BestOfTwo(const T& lhs, const T& rhs, Pred p = Pred())
{
return p(lhs, rhs) ? lhs : rhs;
}
And this worked with no error when I called BestOfTwo(3, 5). But I think the previous style is more convenient and I didn't figure out where it went wrong. What are some suggestions?
Only the second version is correct (if you don't want to specify the Pred parameter manually), but only since C++11. There is already an answer from Angew that clarify why the first version is incorrect, without specification of the Pred parameter.
If you cannot use C++11 you should write two overloads (one with Pred and one without, that use std::less), since default template parameters for function templates are explicitly forbidden in C++98.
template<typename T, typename Pred>
inline const T BestOfTwo(const T& lhs, const T& rhs, Pred p = Pred())
{
//
}
template<typename T>
inline const T BestOfTwo(const T& lhs, const T& rhs)
{
return BestOfTwo<T, std::less<T> >(lhs, rhs);
}
The first version would work if you specified the template arguments explicitly:
BestOfTwo<int, std::less<int>>(3, 5)
The reason is that default function arguments cannot be used to deduce the type for a template parameter.
Related
The original working code
I have a class template with two template parameters and an optimized operator== when the two types are the same and one another condition is satisfied. My original code is as follows (for demonstration purposes I make the generic comparison return false and the one where T1 == T2 return true):
template<typename T1, typename T2>
struct my_class
{
// A few hundred LOCs
};
template<typename U1, typename U2>
bool operator==(my_class<U1, U2> const& lhs, my_class<U1, U2> const& rhs)
{
return false;
}
template<typename U>
auto operator==(my_class<U, U> const& lhs, my_class<U, U> const& rhs)
-> std::enable_if_t<some_condition, bool>
{
return true;
}
The idea is that the first overload of operator== is the default, and when both U1 and U2 are the same type and some_condition is satisfied the second overload is valid and picked as a better match.
The problem
I recently started implementing more and more operators in my generic libraries as hidden friends to avoid some unwanted implicit conversions and to reduce the overload set the compiler has to choose from at namespace scope.
I first tried the most obvious approach to friends, which is to move the definitions as is in the class template and to prefix them with friend:
template<typename T1, typename T2>
struct my_class
{
// A few hundred LOCs
template<typename U1, typename U2>
friend bool operator==(my_class<U1, U2> const& lhs, my_class<U1, U2> const& rhs)
{
return false;
}
template<typename U>
friend auto operator==(my_class<U, U> const& lhs, my_class<U, U> const& rhs)
-> std::enable_if_t<some_condition, bool>
{
return true;
}
};
Much not to my surprise this didn't work and I got redefinition errors. The reason why is explained - along with a standard quote - in this answer.
An attempt
We are always comparing my_class with matching template parameters, so I figured that I could get rid of the inner template<typename U1, typename U2> in the first definition, however it is trickier in the second one since the single template parameter was used to create a more specialized overload of operator==. Another solution would be to put that overload in a specialization of my_class<T, T> but since the class is big I didn't feel like duplicating its contents since pretty much everything else is the same. I could probably introduce another layer of indirection for the common code but I already have a hefty amount of indirection.
Failing that I tried to fallback to good old SFINAE to make sure that T1 and T2 are the same:
template<typename T1, typename T2>
struct my_class
{
// A few hundred LOCs
friend auto operator==(my_class const& lhs, my_class const& rhs)
-> bool
{
return false;
}
friend auto operator==(my_class const& lhs, my_class const& rhs)
-> std::enable_if_t<std::is_same<T1, T2>::value && some_condition, bool>
{
return true;
}
};
For some reason I don't claim to fully understand the second operator== above is actually ill-formed, but we can get around that by adding back some additional defaulted template parameters into the mix:
template<typename T1, typename T2>
struct my_class
{
// A few hundred LOCs
friend auto operator==(my_class const& lhs, my_class const& rhs)
-> bool
{
return false;
}
template<typename U1=T1, typename U2=T2>
friend auto operator==(my_class const& lhs, my_class const& rhs)
-> std::enable_if_t<std::is_same<U1, U2>::value && some_condition, bool>
{
return true;
}
};
This compiles as expected, but comparing two instances of my_class with matching T1 and T2 now returns false because the second overload of operator== is less specialized than the first one. An educated guess tells me that the new template layer is the reason, so I added back template parameters to the first overload of operator== and also an SFINAE condition which is a negation of the other one to make sure that the overload wouldn't be ambiguous with matching T1 and T2:
template<typename T1, typename T2>
struct my_class
{
// A few hundred LOCs
template<typename U1=T1, typename U2=T2>
friend auto operator==(my_class const& lhs, my_class const& rhs)
-> std::enable_if_t<not(std::is_same<U1, U2>::value && some_condition), bool>
{
return false;
}
template<typename U1=T1, typename U2=T2>
friend auto operator==(my_class const& lhs, my_class const& rhs)
-> std::enable_if_t<std::is_same<U1, U2>::value && some_condition, bool>
{
return true;
}
};
This finally gives the intended result while also providing the benefits of hidden friends, but the cost is a bit high in term of readability and maintainability.
Back to the question I originally meant to ask
I tried to explain my problem and rough solution and how I got to it above. My question is: is there a better way to achieve the same result (hidden friends with my code) without having to dive into all the template issues I highlighted above? Can I have such hidden friends while relying on the built-in partial ordering of function templates instead of replacing it with another layer of SFINAE as I did?
Would you accept this?
template<typename T1, typename T2>
struct my_class
{
friend bool operator==(my_class const& lhs, my_class const& rhs)
{
if constexpr (std::is_same_v<T1, T2>) {
return condition;
} else {
return false;
}
}
};
I'm developing a header-only library for automatic/algorithmic differentiation. The goal is to be able to simply change the type of the variables being fed to a function and calculate first and second derivatives. For this, I've created a template class that allows the programmer to select the storage type for the private data members. Included is a snippet below with an offending operator overload.
template <typename storage_t>
class HyperDual
{
template <typename T> friend class HyperDual;
public:
template <typename T>
HyperDual<storage_t> operator+(const HyperDual<T>& rhs) const
{
HyperDual<storage_t> sum;
for (size_t i = 0; i < this->values.size(); i++)
sum.values[i] = this->values[i] + rhs.values[i];
return sum;
}
protected:
std::vector<storage_t> values;
};
Later on, to maximize the versatility, I provide template functions to allow interaction.
template <typename storage_t, typename T>
HyperDual<storage_t> operator+(const HyperDual<storage_t>& lhs, const T& rhs)
{
static_assert(std::is_arithmetic<T>::value && !(std::is_same<T, char>::value), "RHS must be numeric");
return HyperDual<storage_t>(lhs.values[0] + rhs);
}
template <typename storage_t, typename T>
HyperDual<storage_t> operator+(const T& lhs, const HyperDual<storage_t>& rhs)
{
static_assert(std::is_arithmetic<T>::value && !(std::is_same<T, char>::value), "LHS must be numeric");
return HyperDual<storage_t>(lhs + rhs.values[0]);
}
What I'm encountering is that the compiler is trying to instantiate the second non-member template function.
#include "hyperspace.h"
int main()
{
HyperDual<long double> one(1); // There is an appropriate constructor
HyperDual<double> two(2);
one + two;
return 0;
}
I get the static_assert generated error "LHS must be numeric" for this. How would I resolve the ambiguity?
use enable_if_t to make the non-member template can only be applied in the specific context?
template <typename storage_t, typename T, typename = enable_if_t<std::is_arithmetic<T>::value && !(std::is_same<T, char>::value)>>
HyperDual<storage_t> operator+(const HyperDual<storage_t>& lhs, const T& rhs)
{
static_assert(std::is_arithmetic<T>::value && !(std::is_same<T, char>::value), "RHS must be numeric");
return HyperDual<storage_t>(lhs.values[0] + rhs);
}
the static_assert may be duplicated here.
Ok. I found my own issue. It comes down to the difference between static_assert and std::enable_if
Replacing my template declaration and removing static_assert, I achieve equivalent functionality:
template <typename storage_t, typename T,
typename = typename std::enable_if<std::is_arithmetic<T>::value && !std::is_same<T, char>::value>::type>
HyperDual<storage_t> operator+(const T& lhs, const HyperDual<storage_t>& rhs)
{
return HyperDual<storage_t>(lhs + rhs.value());
}
(Small detail, but rhs.values[0] was replaced with rhs.value(). This had nothing to do with the template issue, but was related to member access.
I am trying to make a typed compare function that do some customized comparison for different types.
#include <type_traits>
template <typename T>
bool typedCompare(const T& lhs, const T& rhs)
{
return lhs == rhs; // default case, use ==
}
template <typename T>
typename std::enable_if<std::is_floating_point<T>::value, bool>::type
typedCompare(const T& lhs, const T& rhs)
{
return (lhs - rhs) < 1e-10;
}
int main()
{
typedCompare(1, 1);
typedCompare(1.0, 1.0);
return 0;
}
Here I have a special version for double that compare the difference with a small amount (please ignore the fact that I did not use std::abs()). I have a few other custom types that I need to do some special comparison, and I cannot change their == operator for some reason.
Besides, I still want to have a "catch-all" style function that employs the == operator. My problem is that when trying to compile this code snippet the compiler complains about that typedCompare(1.0, 1.0) is ambiguous, it can choose either of the two functions provided.
Why? And how could I resolve this issue?
Thanks.
Why?
In short, you use SFINAE incorrectly so both function templates are valid when you call typedCompare for doubles.
And how could I resolve this issue?
In this particular case, fix SFINAE to make it work correctly:
template <typename T>
typename std::enable_if<!std::is_floating_point<T>::value, bool>::type
typedCompare(const T& lhs, const T& rhs)
{
return lhs == rhs; // default case, use ==
}
template <typename T>
typename std::enable_if<std::is_floating_point<T>::value, bool>::type
typedCompare(const T& lhs, const T& rhs)
{
return (lhs - rhs) < 1e-10;
}
Please note, that this solution is not that good in terms of customization for many types. Another way is to use tag dispatching:
struct floating_point_tag {};
// other tags
template <typename T>
bool typedCompare(const T& lhs, const T& rhs, floating_point_tag) {
return (lhs - rhs) < 1e-10;
}
// implementations for other tags
template <typename T>
bool typedCompare(const T& lhs, const T& rhs) {
if (std::is_floating_point<T>::value) {
return typedCompare(lhs, rhs, floating_point_tag{});
}
// other checks here
return lhs == rhs;
}
Finally, with C++17 you might make use of if constexpr:
template <typename T>
bool typedCompare(const T& lhs, const T& rhs) {
if constexpr (std::is_floating_point<T>::value) {
return (lhs - rhs) < 1e-10;
} else { // add other if-else here
return lhs == rhs;
}
}
The problem with the code in your question is that when the floating point typedCompare() is SFINAE enabled, collide with the general version because the compiler can't prefer one version over the other.
To solve this problem, I suggest you another way, based on template partial specialization (available only with structs and classes, so need a helper struct)
If you define an helper struct as follows
template <typename T, typename = void>
struct typedCompareHelper
{
static constexpr bool func (T const & lhs, T const & rhs)
{ return lhs == rhs; } // default case, use ==
};
template <typename T>
struct typedCompareHelper<T,
typename std::enable_if<std::is_floating_point<T>::value>::type>
{
static constexpr bool func (T const & lhs, T const & rhs)
{ return (lhs - rhs) < 1e-10; }
};
you avoid the ambiguity problem because the typedCompareHelper specialization in more specialized of the generic one.
You can simply add more specializations for different special cases making only attention in avoiding collisions (different specializations of the same level that apply over the same type).
Your typedCompare() become simply
template <typename T>
bool typedCompare (T const & lhs, T const & rhs)
{ return typedCompareHelper<T>::func(lhs, rhs); }
I like both the solution from #max66 with a helper template and the multiple solutions from #Edgar Rokyan.
Here is another approach that can be used for what you want using a helper template function.
#include <type_traits>
#include <iostream>
#include <string>
// elipsis version is at the bottom of the overload resolution priority.
// it will only be used if nothing else matches the overload.
void typeCompare_specialized(...)
{
std::cout << "SHOULD NEVER BE CALLED!!!\n";
}
template <typename T>
typename std::enable_if<std::is_floating_point<T>::value, bool>::type
typeCompare_specialized(const T& lhs, const T& rhs)
{
std::cout << "floating-point version\n";
return (lhs - rhs) < 1e-10;
}
template <typename T>
typename std::enable_if<std::is_integral<T>::value, bool>::type
typeCompare_specialized(const T& lhs, const T& rhs)
{
std::cout << "integral version\n";
return lhs == rhs;
}
template <typename T>
auto typedCompare(const T& lhs, const T& rhs)
-> typename std::enable_if<std::is_same<bool,decltype(typeCompare_specialized(lhs, rhs))>::value,bool>::type
{
return typeCompare_specialized(lhs, rhs);
}
template <typename T>
auto typedCompare(const T& lhs, const T& rhs)
-> typename std::enable_if<!std::is_same<bool,decltype(typeCompare_specialized(lhs, rhs))>::value,bool>::type
{
std::cout << "catch-all version\n";
return lhs == rhs;
}
int main()
{
typedCompare(1, 1);
typedCompare(1.0, 1.0);
typedCompare(std::string("hello"), std::string("there"));
return 0;
}
Running the above program will yield the following output:
integral version
floating-point version
catch-all version
Again, I would prefer to use one of the previous answers mentioned. I include this possibility for completeness.
I would also like to add that you should make sure your typeCompare_specialized() template versions should not have any overlap, otherwise you could get a compiler error declaring that there are multiple candidate overloads to use.
I want to create a shared_ptr content-comparison functor to stand in for std::less<T> in associative containers and std algorithms. I've seen several examples of custom comparators that use the following (or similar) model:
template <typename T>
struct SharedPtrContentsLess {
bool operator()(const boost::shared_ptr<T>& lhs,
const boost::shared_ptr<T> rhs) const {
return std::less<T>(*lhs, *rhs);
//or: return (*lhs) < (*rhs);
}
//defining these here instead of using std::binary_functor (C++11 deprecated)
typedef boost::shared_ptr<T> first_argument_type;
typedef boost::shared_ptr<T> second_argument_type;
typedef bool return_type;
};
But why wouldn't I want to instead extend std::less? Like so:
template <typename T>
struct SharedPtrContentsLess : public std::less< boost:shared_ptr<T> > {
bool operator()(const boost::shared_ptr<T>& lhs,
const boost::shared_ptr<T> rhs) const {
return std::less<T>(*lhs, *rhs);
}
};
Does this buy me anything at all?
I would think this gets me the typedefs for free, as though I was extending the deprecated std::binary_function. In C++03, I actually would be extending it through std::less. However, this would also be portable from C++03 to C++11/14 and even C++17 when std::binary_function will be removed, as it just follows the changes in std::less.
I've read a bunch of answers on StackOverflow regarding std::less use, custom comparison functors, and even some of the Standard specs and proposals. I see specializations of std::less and guidance not to extend STL containers, but I can't seem to find any examples of extending std::less or guidance against it. Am I missing an obvious reason not to do this?
EDIT: Removed C++11 tag, as it is causing confusion to the answerers. I am hoping to get forward-portability, but C++03 is required. If you provide a C++11-only answer for others to use (totally fine), please note that.
You can create a reusable template towards any dereferencable object (i.e. any (smart) pointer) by simply forwarding the call to std::less or any other comparable object.
// c++11
template<template<class> Op, class T> struct deref_mixin;
template<template<class> Op, class T>
struct deref_mixin {
auto operator()(const T &l, const T &r) const
-> decltype(std::declval<Op<T>>()(*l, *r)) {
return Op<T>{}(*l, *r);
}
};
template<template<class> Op>
struct deref_mixin<Op, void> {
template<class T, class U>
auto operator()(const T &l, const U &r) const
-> decltype(std::declval<Op<T>>()(*l, *r)) {
return Op<void>{}(*l, *r);
}
};
template<class T> using less_deref = deref_mixin<std::less, T>;
template<class T> using greater_deref = deref_mixin<std::greater, T>;
template<class T> using my_comparator_deref = deref_mixin<my_comparator, T>;
// c++03
template<template<class> Op, class T>
struct deref_mixin {
bool operator()(const T &l, const T &r) const {
Op<T> op;
return op(*l, *r);
}
};
// Technically, the void template partial specialization isn't defined in c++03, but it should have been :)
template<template<class> Op>
struct deref_mixin<Op, void> {
template<class T, class U>
bool operator()(const T &l, const U &r) const {
Op<void> op;
return op(*l, *r);
}
};
template<class T> struct less_deref : deref_mixin<std::less, T> {};
As you said in your question if you inherit from std::less the you would get the three typedefs that are in std::less. What I like most about inheriting from it is it describes your intent. When I see
struct some_non_specific_name : std::less<some_type>
I know right there that this is a functor that is going to behave as an < for some_type. I don't have to read the struct body to find out anything.
As far as I can see, you are not missing any disadvantage. As you mentioned, you would automatically get the typedefs. The operator< has to be defined in both cases, and there is no difference in its implementation.
There is one thing that you might get that you might find neat, bad or just not applicable to your usecase (from here and here): there is a specialization of std::less for std::less<void> that has a template operator< deduces the return type of the operator< for the given arguments.
Unless you intend to use a SharedPtrContentsLess<void> (which probably doesn't make sense at all), both solutions would be equivalent.
I'd write a deref_less. First, my_less that smartly calls std::less:
struct my_less {
template<class Lhs, class Rhs,
class R = std::result_of_t< std::less<>( Lhs const&, Rhs const& ) >
// class R = decltype( std::declval<Lhs const&>() < std::declval<Rhs const&>() )
>
R operator()(Lhs const&lhs, Rhs const&rhs)const{
return std::less<>{}(lhs, rhs); // or lhs<rhs
}
// exact same type uses `std::less<T>`:
template<class T,
class R = std::result_of_t< std::less<>( T const&, T const& ) >
>
R operator()(T const& lhs, T const& rhs)const{
return std::less<T>{}(lhs, rhs);
}
template<class Lhs, class Rhs,
std::enable_if_t< std::is_base_of<Lhs, Rhs>{} && !std::is_same<Lhs, Rhs>{} >* = nullptr
>
bool operator()(Lhs const* lhs, Rhs const* rhs)const{
return std::less<Lhs const*>{}(lhs, rhs);
}
template<class Lhs, class Rhs,
std::enable_if_t< std::is_base_of<Rhs, Lhs>{} && !std::is_same<Lhs, Rhs>{} >* = nullptr
>
bool operator()(Lhs const* lhs, Rhs const* rhs)const{
return std::less<Rhs const*>{}(lhs, rhs);
}
template<class Lhs, class Rhs,
std::enable_if_t<
!std::is_base_of<Rhs, Lhs>{}
&& !std::is_base_of<Lhs, Rhs>{}
&& !std::is_same<Lhs, Rhs>{}
>* = nullptr
>
bool operator()(Lhs const* lhs, Rhs const* rhs)const = delete;
};
then, a deref_less that does a * then calls myless:
struct deref_less {
template<class Lhs, class Rhs,
class R = std::result_of_t< my_less( decltype(*std::declval<Lhs>()), decltype(*std::declval<Rhs>()) ) >
>
R operator()(Lhs const& lhs, Rhs const&rhs)const {
return my_less{}( *lhs, *rhs );
}
};
in C++14, but everything I used is easy to replace (std::less<> can be replaced with decltype and <s for example).
Because std::less lacks a virtual destructor (i.e. implicit destructor only), inheriting from it could technically lead to undefined behavior. Since neither type contains any data members, destruction should work no matter how the object is referenced, but the standard forbids polymorphic deletion through static destructors because it presents a strong possibility of problems (slicing, incomplete deletion) in most cases.
See this answer:
Thou shalt not inherit from std::vector
Seems kind of a tough problem because you can't add new member functions to vector. This form avoids the least amount of copies:
std::vector<T>& operator+=(std::vector<T>& lhs, const std::vector<T>& rhs)
But fails for self-assignment, so the only one that works for self-assignment is:
template <typename T>
std::vector<T>& operator+=(std::vector<T>& lhs, std::vector<T> rhs)
{
lhs.insert(lhs.end(), rhs.begin(), rhs.end());
return lhs;
}
But this requires an extra copy. What's the correct way of doing this?
It was ambiguous in my question that the above forms "don't work" because they appear to work for ints (although not for std::strings). It was pointed out this is because it's undefined behavior.
The problem with:
template <typename T>
std::vector<T>& operator+=(std::vector<T>& lhs, const std::vector<T>& rhs)
{
lhs.insert(lhs.end(), rhs.begin(), rhs.end());
return lhs;
}
is not the signature, it's passing iterators to insert that become invalid before insert has completed.
Just use the correct technique for appending a vector to itself, and no extra copy is needed.
template <typename T>
void concat_in_place(std::vector<T>& lhs, const std::vector<T>& rhs)
{
auto left_count = lhs.size();
auto right_count = rhs.size();
lhs.resize(left_count + right_count);
std::copy_n(rhs.begin(), right_count, lhs.begin() + left_count);
}
You should not, as a matter of policy, overload two different std types with an operator between them. It may be undefined behavior: the standard is ambiguous.
If you want operator syntax on std containers, I would recommend named operators. It is also more clear, as operators on a vector could be container operators or element-wise operators, which is why they are missing by default. v +append= v2; is clearly appending. (create a static append object, and overload its lhs and rhs operators with your vector, and use expression templates in the intermediate steps)
// mini named operator library. Only supports + for now:
template<class Kind>
struct named_operator {};
template<class OP, class LHS> struct plus_ {
LHS lhs;
template<class RHS>
decltype(auto) operator=(RHS&&rhs)&&{
return plus_assign(std::forward<LHS>(lhs), OP{}, std::forward<RHS>(rhs));
}
template<class RHS>
decltype(auto) operator+(RHS&&rhs)&&{
return plus(std::forward<LHS>(lhs), OP{}, std::forward<RHS>(rhs));
}
};
template<class Tag, class LHS>
plus_<Tag,LHS> operator+( LHS&& lhs, named_operator<Tag> ) {
return {std::forward<LHS>(lhs)};
}
// creating a named operator:
static struct append_tag:named_operator<append_tag> {} append;
// helper function, finds size of containers and arrays:
template<class T,std::size_t N>
constexpr std::size_t size( T(&)[N] ) { return N; }
template<class C>
constexpr auto size(C&& c)->decltype(c.size()) { return c.size(); }
// implement the vector +append= range:
template<class T, class A, class RHS>
std::vector<T,A>& plus_assign(std::vector<T,A>&lhs, append_tag, RHS&& rhs) {
auto rhs_size = size(rhs);
lhs.reserve(lhs.size()+rhs_size);
using std::begin; using std::end;
copy_n( begin(rhs), rhs_size, back_inserter(lhs) );
return lhs;
}
// implement container +append+ range:
template<class LHS, class RHS>
LHS plus( LHS lhs, append_tag, RHS&& rhs ) {
using std::begin; using std::end; using std::back_inserter;
copy_n( begin(rhs), size(rhs), back_inserter(lhs) );
return std::move(lhs);
}
live example
Note that std::vector<int> +append= std::list<int> +append+ std::array<double, 3> works with the above code.