C++: wrong template function overload selection - c++

I would like to implement some lazy evaluation in my code. Here is a snippet representative of what I would like to do:
#include <Eigen/Dense>
#include <iostream>
template <int N, int M>
struct S {
template <typename T>
S &operator=(const T &src) {
v = src();
return *this;
}
Eigen::Matrix<double, N, M> v;
};
// If I comment this one the code compiles
template <typename Lhs, typename Rhs>
auto operator+(const Lhs &lhs, const Rhs &rhs) {
return [&]() { return lhs() + rhs(); };
}
template <typename Lhs, int N, int M>
auto operator+(const Lhs &lhs, const S<N, M> &s) {
return [&]() { return lhs() + s.v; };
}
template <typename Rhs, int N, int M>
auto operator+(const S<N, M> &s, const Rhs &rhs) {
return [&]() { return s.v + rhs(); };
}
template <int N0, int M0, int N1, int M1>
auto operator+(const S<N0, M0> &s0, const S<N1, M1> &s1) {
return [&]() { return s0.v + s1.v; };
}
int main() {
static constexpr int n = 4;
S<n, n> s0, s1;
S<Eigen::Dynamic, Eigen::Dynamic> s2;
s1.v = 2. * Eigen::Matrix<double, n, n>::Ones();
s2.v = 3. * Eigen::Matrix<double, n, n>::Ones();
s0 = s1 + s2;
std::cout << s0.v << std::endl;
return 0;
}
I got a compilation error which relates to the fact that when implementing s1 + s2, the first overload of the + operator is selected (and if I comment it, the code works). It seems that for the compiler the first one is considered as the more specialized, whereas to my common sense the last one is the more specialized: the first one accepts whatever arguments, whereas the last one only accepts type related to the template class S.
What is the explanation to that? How can I fix this snippet to make it work while keeping the first overload (which will be required for the lazy evaluation of 'nested' expressions)?
Many thanks!
EDIT: from the answer and comments, I now understand that the problem is related to a conflict between the first overload of the + operator in the snippet and the some overload of the + operator in the Eigen library. The simplified snippet following illustrate that:
#include <Eigen/Dense>
#include <iostream>
template <typename Lhs, typename Rhs>
auto operator+(const Lhs &lhs, const Rhs &rhs) {
return [&]() { return lhs() + rhs(); };
}
int main() {
Eigen::Matrix<double, 4, 4> m0, m1, m2;
m2=m0+m1;
return 0;
}
Here, the + operator of the snippet is selected, whereas I expected that one of the Eigen's would be selected... Why is that?

It looks like the first overload is selected, but not for s1 + s2. That selects the 4th overload. However, inside that 4th overload you have s0.v + s1.v;, and .v is not an S<N, M>

Related

Template constraints on operator overloading does not work as expected

#include <tuple>
#include <utility>
template<typename T>
struct is_tuple_like : std::false_type {};
template<typename... Ts>
struct is_tuple_like<std::tuple<Ts...>> : std::true_type {};
template<typename T, typename U>
struct is_tuple_like<std::pair<T, U>> : std::true_type {};
template<typename T>
concept tuple_like = is_tuple_like<T>::value;
template<tuple_like L, tuple_like R, int N = std::tuple_size_v<L>>
auto operator*(const L &lhs, const R &rhs) { return 0; }
enum { Enum };
int main()
{
Enum * Enum; // causes compilation error
return 0;
}
You can run the code here: http://coliru.stacked-crooked.com/a/f65e333060f40e60
I have defined a concept so-called tuple_like and overloaded operator*() using the concept.
Then, If I multiply enums, my overloaded operator*() for tuple_like is picked up and the compiler complains missing std::tuple_size for enum.
What did I do wrong here and how can I fix it without overload for each class templates - std::tuple and std::pair?
FYI, even if it's unusual, I cannot remove the part of multiplying enums because it's not my code.
Turn tuple_size_v into tuple_size::value to enable SFINAE
template<tuple_like L, tuple_like R, int N = std::tuple_size<L>::value>
auto operator*(const L &lhs, const R &rhs) { return 0; }
However, I don't see any value in declaring an extra template parameter N here. The type that satisfies tuple_like already guarantees that tuple_size_v is a valid value.
It would be more appropriate to move N into the function body
template<tuple_like L, tuple_like R>
auto operator*(const L &lhs, const R &rhs) {
constexpr auto N = std::tuple_size_v<L>; // guaranteed to work
// uses N below
}
Looks like gcc can't handle SFINAE for default values of template arguments. This workaround fixes it:
template<tuple_like L, tuple_like R, int N>
auto operator*(const L &lhs, const R &rhs) { return 0; }
template<tuple_like L, tuple_like R>
auto operator*(const L &lhs, const R &rhs) { return operator*<L, R, std::tuple_size_v<L>>(lhs, rhs); }

How do I expand integer_sequence?

I have a function which looks like this:
template <typename T, std::size_t... I>
std::ostream& vector_insert(std::ostream& lhs, const char* delim, const T& rhs, std::index_sequence<I...>) {
std::ostream_iterator<float> it(lhs, delim);
((*it++ = at(rhs, I)), ...);
return lhs;
}
This is my final attempt and I'm still failing on my expansion of the integer_sequence I'm hoping someone can tell me how to write a line that will effectively expand to:
*it++ = at(rhs, 0U), *it++ = at(rhs, 1U), *it++ = at(rhs, 2U)
Other things I've tried are:
*it++ = at(rhs, I...)
*it++ = at(rhs, I)...
(*it++ = at(rhs, I))...
All of them are giving me the error:
error C3520: I: parameter pack must be expanded in this context
How do I expand this thing?
EDIT:
#AndyG has pointed out that this seems to be a visual-studio-2017 bug.
This seems like a compiler bug with Visual C++. I'm not aware of any easy fix for it other than simplifying the expression in which the parameter pack is expanded. Converting to a recursive approach seems to reliably work around the problem. For example :
#include <array>
#include <iostream>
#include <iterator>
template <typename T>
const auto& at(const T& v, size_t i) { return v[i]; }
// End of recursion
template<class T>
void vector_insert_impl(std::ostream_iterator<int> &, const char*, const T&)
{}
// Recursion case
template<class T, std::size_t N, std::size_t... I>
void vector_insert_impl(std::ostream_iterator<int> & iter, const char* delim, const T&rhs)
{
*iter++ = at(rhs, N);
// Continue the recursion
vector_insert_impl<T, I...>(iter, delim, rhs);
}
template <typename T, std::size_t... I>
std::ostream& vector_insert(std::ostream& lhs, const char* delim, const T& rhs, std::index_sequence<I...>)
{
std::ostream_iterator<int> it(lhs, delim);
// Call the recursive implementation instead
vector_insert_impl<T, I...>(it, delim, rhs);
return lhs;
}
int main() {
std::array<int, 5> v = { 1, 2, 3, 4, 5 };
vector_insert(std::cout, " ", v, std::make_index_sequence<v.size()>());
}
Here, the parameter pack I is only expanded in the context of providing template parameters which VC++ has no trouble with.

C++ Vector Template Per-Component Operations

I'm rewriting the vector math portion of my project, and I'd like to generalize vectors by their type and number of dimensions. A vector<T, N> represents an N dimensional vector of type T.
template<typename T, int N>
struct vector {
T data[N];
};
I'll need to rewrite many math functions, most of which will operate on a per-component basis. The straightforward implementation of the addition operator is shown below.
template<typename T, int N>
vector<T, N> operator+(vector<T, N> lhs, vector<T, N> rhs) {
vector<T, N> result;
for (int i = 0; i < N; i++) {
result[i] = lhs[i] + rhs[i];
}
return result;
}
My question: Is there a way (via template-trickery?) to implement this without the use of a for loop and a temporary variable? I understand that the compiler would most likely unroll the loop and optimize it away. I just don't like the idea of having all of my performance-critical math functions implemented this way. They will all be inlined and in the header, so having many of these functions would also make for a big ugly header file.
I'm wondering if there is a way to do this which would produce more optimal source code. Possibly a way that works like variadic templates do. Something along the lines of this.
template<typename T, int N>
vector<T, N> operator+(vector<T, N> lhs, vector<T, N> rhs) {
return vector<T, N>(lhs[0] + rhs[0], lhs[1] + rhs[1]...);
}
One way to do this is via lower level "map" functions:
Here's a complete working example
#include <iostream>
#include <math.h>
template<typename T, int N>
struct vector {
T data[N];
};
First declare your worker "map" functions - I've got 3 here map, map2, foreach.
template<typename T, int N, typename FN>
static void foreach(const vector<T,N> & vec, FN f) {
for(int i=0; i<N ;++i) {
f(vec.data[i]);
}
}
template<typename T, int N, typename FN>
static auto map(const vector<T,N> & vec, FN f) -> vector<decltype(f(T(0))), N> {
vector<decltype(f(T(0))), N> result;
for(int i=0; i<N ;++i) {
result.data[i] = f(vec.data[i]);
}
return result;
}
template<typename T1, typename T2, int N, typename FN>
static auto map2(const vector<T1,N> & vecA,
const vector<T2,N> & vecB,
FN f)
-> vector<decltype(f(T1(0), T2(0))), N> {
vector<decltype(f(T1(0), T2(0))), N> result;
for(int i=0; i<N ;++i) {
result.data[i] = f(vecA.data[i], vecB.data[i]);
}
return result;
}
Now use the helpers to define your higher level functions via lambdas. I'll define binary +, binary -, unary - and e^x. Oh and operator<< so we can see what is going on.
I'm pretty sure there's a better alternative to the lambdas used in operator+ and operator-, but I can't remember them
template<typename T, int N>
vector<T,N> operator+(const vector<T,N> &lhs, const vector<T,N> &rhs) {
return map2(lhs, rhs, [](T a,T b) { return a+b;} );
}
template<typename T, int N>
vector<T,N> operator-(const vector<T,N> &lhs, const vector<T,N> &rhs) {
return map2(lhs, rhs, [](T a,T b) { return a-b;} );
}
template<typename T, int N>
vector<T,N> operator-(const vector<T,N> &vec) {
return map(vec, [](T a) { return -a;} );
}
template<typename T, int N>
auto exp(const vector<T,N> &vec) -> vector<decltype(exp(T(0))), N> {
return map(vec, [](T a) { return exp(a); } );
}
template<typename T, int N>
std::ostream & operator<<(std::ostream& os, const vector<T,N> &vec) {
os<<"{";
foreach(vec, [&os](T v) { os<<v<<", "; } );
os<<"}";
return os;
}
Now look how they work just fine...
int main() {
vector<int, 5> v1 = {1,2,3,4,5};
vector<int, 5> v2 = {2,4,6,8,10};
std::cout<<v1 << " + " << v2 << " = " << v1+v2<<std::endl;
std::cout<<v1 << " - " << v2 << " = " << v1-v2<<std::endl;
std::cout<<" exp( - " << v2 << " )= " << exp(-v1)<<std::endl;
}
You can do this and I'll point you towards a solution (which compiles and runs). You are looking to get rid of the loop, preferably by inlining it in hopes the compiler will optimize things for you.
In practice I have found it sufficient to specify the dimensions needed, i.e. N = 3, 4, 5 because this allows finer grained control over what the compiler does than doing what you asked for. However you can use recursion and partial template specialization to implement your operators. I have illustrated addition.
So instead of this:
template<typename T, int N>
vector<T, N> operator+(vector<T, N> lhs, vector<T, N> rhs) {
vector<T, N> result;
for (int i = 0; i < N; i++) {
result[i] = lhs[i] + rhs[i];
}
return result;
}
You want code that effectively does this:
template<typename T, int N>
vector<T, N> operator+(vector<T, N> lhs, vector<T, N> rhs) {
vector<T, N> result;
result[0] = lhs[0] + rhs[0];
result[1] = lhs[1] + rhs[1];
...
result[N-1] = lhs[N-1] + rhs[N-1];
return result;
}
if N is 1, it is pretty easy you just want this...
template
vector operator+(vector lhs, vector rhs) {
vector result;
result[0] = lhs[0] + rhs[0];
return result;
}
and if N is 2, it is pretty easy you just want this...
template
vector operator+(vector lhs, vector rhs) {
vector result;
result[0] = lhs[0] + rhs[0];
result[1] = lhs[1] + rhs[1];
return result;
}
The easiest way is to simply define this up to as many N as you expect to use and not the answer you are looking for because you probably don't need more than N=5 or N=6 in practice right?
However, you can also use partial template specialization and recursion to get there. Consider this struct, which recursively calls itself and then assigns the index:
template<typename T, int N, int IDX>
struct Plus
{
void operator()(vector<T,N>& lhs, vector<T,N>& rhs, vector<T,N>& result)
{
Plus<T,N,IDX-1>()(lhs,rhs,result);
result.data[IDX] = lhs.data[IDX] + rhs.data[IDX];
}
};
and this partial specialization which appears to do nothing, but handles the case when the index is 0 and ends the recursion:
template<typename T, int N>
struct Plus<T,N,-1>
{
void operator()(vector<T,N>& lhs, vector<T,N>& rhs, vector<T,N>& result)
{
//noop
}
};
and finally this implementation of operator+ which instantiates Plus and calls it:
template<typename T, int N>
vector<T, N> operator+(vector<T, N> lhs, vector<T, N> rhs) {
vector<T, N> result;
Plus<T,N,N-1>()(lhs,rhs,result);
return result;
}
You'll need to turn this into an operator to make it more general purpose but you get the idea. However this is mean to the compiler and it may take awhile in big projects even if it is super cool. In practice I have found that hand typing the overloads you want or writing script code to generate the C++ results in a more debuggable experience and code that in the end is simpler to read and easier for the compiler to optimize. More specifically if you write a script to generate the C++ you can include the SIMD intrinsics in the first place and not leave things to chance.
Firstly, compiler would probably unroll the loop.
Secondly, for better performance, pass your argument by const reference instead of by value to avoid extra copies.
And to answer your question, you may use std::index_sequence to construct in place, something like:
namespace detail
{
template<typename T, int N, std::size_t...Is>
vector<T, N> add(std::index_sequence<Is...>,
const vector<T, N>& lhs,
const vector<T, N>& rhs)
{
return {{ (lhs[Is] + rhs[Is])... }};
}
}
template<typename T, int N>
vector<T, N> operator+(const vector<T, N>& lhs, const vector<T, N>& rhs) {
return detail::add(std::make_index_sequence<N>{}, lhs, rhs);
}
Demo

operator overloading with multiple templates

template <class T>
class A
{
private:
T m_var;
public:
operator T () const { return m_var; }
........
}
template<class T, class U, class V>
const A<T> operator+ (const U& r_var1, const V& r_var2)
{ return A<T> ( (T)r_var1 + (T)r_var2 ); }
The idea is to overload the + operator once (instead of three) for the cases:
number + A, A + number, A + A (where number is of type T, the same as m_var).
An interesting case would be if m_var is e.g. int and r_var is long.
Any helps would be highly appreciated. Thank you.
The common pattern to achieve what you want is to actually perform it in the opposite direction: provide an implicit conversion from T to the template and only define the operator for the template.
template <typename T>
struct test {
T m_var;
test( T const & t ) : m_var(t) {} // implicit conversion
test& operator+=( T const & rhs ) {
m_var += rhs.m_var;
}
friend test operator+( test lhs, test const & rhs ) { // *
return lhs += rhs;
}
};
// * friend only to allow us to define it inside the class declaration
A couple of details on the idiom: operator+ is declared as friend only to allow us to define a free function inside the class curly braces. This has some advantages when it comes to lookup for the compiler, as it will only consider that operator if either one of the arguments is already a test.
Since the constructor is implicit, a call test<int> a(0); test<int> b = a + 5; will be converted into the equivalent of test<int> b( a + test<int>(5) ); Conversely if you switch to 5 + a.
The operator+ is implemented in terms of operator+=, in a one-liner by taking the first argument by value. If the operator was any more complex this would have the advantage of providing both operators with a single implementation.
The issue with your operator+ is you have 3 template parameters, one for the return type as well as the cast, but there is no way for the compiler to automatically resolve that parameter.
You are also committing a few evils there with casts.
You can take advantage of the that if you define operator+ as a free template function in your namespace it will only have effect for types defined in that namespace.
Within your namespace therefore I will define, using just T and U
template< typename T >
T operator+( const T & t1, const T& t2 )
{
T t( t1 );
t += t2; // defined within T in your namespace
return t;
}
template< typename T, typename U >
T operator+( const T& t, const U& u )
{
return t + T(u);
}
template< typename T, typename U >
T operator+( const U& u, const T& t )
{
return T(u) + t;
}
a + b in general is not covered by this template unless one of the types of a and b is in the namespace where the template was defined.
You should not overload op+ for unrelated types that you know nothing about – this can break perfectly working code that already exists. You should involve your class as at least one of the parameters to the op+ overload.
If you don't want an implicit conversion from T to A<T>, then I would just write out the overloads. This is the clearest code, and isn't long at all, if you follow the "# to #=" overloading pattern:
template<class T>
struct A {
explicit A(T);
A& operator+=(A const &other) {
m_var += other.m_var;
// This could be much longer, but however long it is doesn't change
// the length of the below overloads.
return *this;
}
A& operator+=(T const &other) {
*this += A(other);
return *this;
}
friend A operator+(A a, A const &b) {
a += b;
return a;
}
friend A operator+(A a, T const &b) {
a += A(b);
return a;
}
friend A operator+(T const &a, A b) {
b += A(a);
return b;
}
private:
T m_var;
};
C++0x solution
template <class T>
class A
{
private:
T m_var;
public:
operator T () const { return m_var; }
A(T x): m_var(x){}
};
template<class T,class U, class V>
auto operator+ (const U& r_var1, const V& r_var2) -> decltype(r_var1+r_var2)
{
return (r_var1 + r_var2 );
}
int main(){
A<int> a(5);
a = a+10;
a = 10 + a;
}
Unfortunately changing template<class T,class U, class V> to template<class U, class V> invokes segmentation fault on gcc 4.5.1. I have no idea why?

Commutative operator overloading + of 2 different objects

I have 2 classes which represent a matrix:
1. RegularMatrix - O(n^2) representation
2. SparseMatrix - a matrix that is represented as linked list (without zeros).
lets say i have:
RegularMatrix a;
SparseMatrix b;
i want to be able to do:
a+b;
and also:
b+a;
so i'm overloading the + operator. My question is, since I want the addition to be commutative (a+b = b+a), do i need to implement 2 overloadings, one for each case?
RegularMatrix operator+(const RegualarMatrix &, const SparseMatrix &);
RegularMatrix operator+(const SparseMatrix & ,const RegualarMatrix &);
or is there a general form which the compiler decides by itself?
Thank you
Yes you need both versions. But you can forward the one to the other, if the operation really is commutative
RegularMatrix operator+(const SparseMatrix &a, const RegualarMatrix &b) {
return b + a;
}
Both versions are required, just write after first overload:
RegularMatrix operator+(const SparseMatrix &a, const RegualarMatrix &b)
{
return operator+(b,a);
}
or simpler version:
RegularMatrix operator+(const SparseMatrix &a, const RegualarMatrix &b)
{
return b + a;
}
Unless you have a huge amount of operators with complicated signatures that all need to be duplicated for commutative behaviour, I would just use the solution from the accepted answer. However, if you really hate repeating your code or want to make it work just for the sake of it:
#include <iostream> // std::cout
#include <utility> // std::pair
#include <type_traits> // std::remove_cvref_t
#include <concepts> // std::same_as
// These two utilities will be used for all commutative functions:
template <typename T, typename U, typename V, typename W>
concept commutative =
(std::same_as<std::remove_cvref_t<T>, V> && std::same_as<std::remove_cvref_t<U>, W>) ||
(std::same_as<std::remove_cvref_t<U>, V> && std::same_as<std::remove_cvref_t<T>, W>);
template <typename V, typename W, typename T, typename U>
requires commutative<T, U, V, W>
constexpr decltype(auto) order (T && a, U && b) {
if constexpr (std::same_as<std::remove_cvref_t<T>, V>)
return std::pair{std::forward<T>(a), std::forward<U>(b)};
else
return std::pair{std::forward<U>(b), std::forward<T>(a)};
}
// Here goes the use-case:
struct A {
int aval;
};
struct B {
int bval;
};
// This template declaration allows two instantiations:
// (A const &, B const &) and (B const &, A const &)
template <typename T, commutative<T, A, B> U>
A operator + (T const & first, U const & second) {
// But now we need to find out which is which:
auto const & [a, b] = order<A, B>(first, second);
return {.aval = a.aval + b.bval};
}
// Just to test it:
int main () {
A a = {.aval = 1};
B b = {.bval = 2};
A c = a + b;
A d = b + a;
std::cout << c.aval << '\n';
std::cout << d.aval << '\n';
}
If template <typename T, commutative<T, A, B> U> and auto const & [a, b] = order<A, B>(first, second); is less boilerplate code than repeating the whole definition with return b + a; for your scenario, then I guess it could be useful.