Disable temporary binding of Eigen expression to const references - c++

I am trying to write a function that accepts only lvalue Eigen expressions passed via const references. My first idea was to keep only the overload const Eigen::MatrixBase<Derived>& and delete the Eigen::MatrixBase<Derived>&& one. To my surprise, the deleted function was not part of the overload candidate set. So I tried the code below
#include <iostream>
#include <Eigen/Dense>
#define PRINT_MY_NAME std::cout << __PRETTY_FUNCTION__ << '\n'
template<typename Derived>
void f(const Eigen::MatrixBase<Derived>&) // (1)
{
PRINT_MY_NAME;
}
template<typename Derived>
void f(Eigen::MatrixBase<Derived>&&) // (2)
{
PRINT_MY_NAME;
}
int main()
{
Eigen::MatrixXd A;
f(A); // invokes (1)
f(A + A); // invokes also (1) !!!
}
which outputs (gcc5.2)
void f(const Eigen::MatrixBase&) [with Derived = Eigen::Matrix < double, -1, -1>]
void f(const Eigen::MatrixBase&) [with Derived = Eigen::CwiseBinaryOp < Eigen::internal::scalar_sum_op < double>, const Eigen::Matrix < double, -1, -1>, const Eigen::Matrix < double, -1, -1> >]
so clearly the rvalue overload is not considered. It is now clear for me that the second one is not a better match, since I pass a rvalue Eigen expression, which is convertible to Eigen::MatrixBase<>, but is not the exact same type. Now comes my question:
How can I disable or detect rvalue Eigen expressions passed as parameters for f? The issue is that an expression can have an arbitrary type (Eigen uses expression templates), like CwiseBinaryOp<...CwiseBinaryOp<...>> and so on. This is part of a larger problem in which I have a utility make-like function which takes a lvalue and binds it to a const reference expression in a class. If the expression is a rvalue, then all bets are off, since reference binding is not propagating via a constructor argument, so I want to forbid passing rvalue Eigen expressions.

I think I found what was going on: the result of the expression template A + A was a const, so there was a CV-mismatch. Adding const to the second overload does it:
template<typename Derived>
void f(const Eigen::MatrixBase<Derived>&&) // (2)
{
PRINT_MY_NAME;
}

Related

template parameter inference for std pair [duplicate]

This question already has answers here:
Template argument deduction for implicit pair
(2 answers)
Closed 1 year ago.
In the code below, X x1{{100,5.55}, "abc"} fails to compile ("candidate template ignored: couldn't infer template argument 'T1'").
I believe that this is because, template deduction does not consider implicit conversions, so the compiler does not treat the first argument as a std::pair.
Is there any way to make the X x1{{100,5.55}, "abc"} syntax work? I want to avoid explicit use of std::pair in the argument, because I am refactoring a large code base where this expression is used in many places.
(In the current code, the constructor is not templated, because it uses std::variant to create an omnibus class that handles multiple types with implicit constructors for int, double, std::string, etc. I am trying to replace this class with a templated constructor that preserves the underlying data types, to improve type safety.)
#include <utility>
struct X
{
template <class T1, class T2> X(std::pair<T1,T2>, const char*){}
};
int main()
{
X x{std::pair{100,5.55}, "abc"}; // OK
// X x1{{100,5.55}, "abc"}; // Compile error
}
I don't have a perfect solution, but if I am understanding your constraints well enough, it is not a big deal if new code needs to explicitly use std::pair{}, but old code has to remain compatible.
What about introducing a simple compatibility struct and (ab)using aggregate initialization to provide a constructor overload?
#include <utility>
#include <iostream>
struct X
{
template <class T1, class T2> X(std::pair<T1,T2>, const char*)
{
std::cout << __PRETTY_FUNCTION__ << '\n';
}
struct LegacyPair
{
int a;
double b;
};
X(LegacyPair p, const char* text) :
X(std::pair{p.a, p.b}, text)
{}
};
int main()
{
X x1{std::pair{100,5.55}, "abc"};
X x2{{100,5.55}, "abc"};
//X x3{{3.14,5}, "abc"}; // error
X x4{std::pair{3.14,5}, "abc"};
}
This outputs:
X::X(std::pair<_T1, _T2>, const char*) [with T1 = int; T2 = double] for x1
X::X(std::pair<_T1, _T2>, const char*) [with T1 = int; T2 = double] for x2
An error for x3 (better than accidentally constructing a std::pair<int, double>). Because {} is aggregate initialization here, implicit conversion is not allowed, so this overload gets discarded. Which is good!
X::X(std::pair<_T1, _T2>, const char*) [with T1 = double; T2 = int] for x4

cannot bind non-const lvalue reference of type ‘T&’ to an rvalue of type ‘T’ t++ which std::atomic<T>

this is my code
#include <iostream>
#include <atomic>
using namespace std;
class T{
public:
int i = 0;
friend T operator++( T& t, int);
};
T operator++( T& t, int){
t.i++;
return T(); // please ignore this. I only care for it to compile right now
}
int main() {
atomic<T> t;
t++;
return 0;
}
I am trying to use atomic with a custom class B but im getting error:
*Compilation error #stdin compilation error #stdout 0s 4400KB
prog.cpp: In function ‘int main()’:
prog.cpp:21:3: error: cannot bind non-const lvalue reference of type ‘T&’ to an rvalue of type ‘T’
t++;
^~
In file included from prog.cpp:2:
/usr/include/c++/8/atomic:202:7: note: after user-defined conversion: ‘std::atomic<_Tp>::operator _Tp() const [with _Tp = T]’
operator _Tp() const noexcept
^~~~~~~~
prog.cpp:11:4: note: initializing argument 1 of ‘T operator++(T&, int)’
T operator++( T& t, int){
^~~~~~~~*
I am using friend to avoid using explicit conversion
(T(t))++;
if I am defining the operator++ with const like this:
friend T operator++(const T& t, int);
it compiles but then of course its useless to me.
When you do t++, it is the same as (t.operator T())++, which is equivalent to (t.load(std::memory_order_seq_cst))++.
This returns a copy of the value held by the atomic, which is an rvalue. Incrementing an rvalue doesn't make sense (It is destroyed immediately), so perhaps you want to lock, load and then store?
The std::atomic<T>::operator++ operators are only defined for integers and pointers (see the fine green print here).
The compiler attempts to invoke std::atomic<T>::operator T to obtain a temporary copy of the contained T instance, and then call your own operator ++ on it, which therefore requires a const reference parameter. atomic provides no way of locking, calling your own operator, and unlocking. Since this could lead to deadlocks (if your operator acquires some other lock), this would subvert atomic's purpose anyways.
You probably need to use a lock like std::mutex explicitly.

Custom class pointer as unordered map key

I'm trying to implement unordered_map with pointer to custom class as key and integer as value.
I thought pointer is just an address, so I wouldn't have to create comparison template for unordered_map, since map would compare between addresses. But I get compile error.
My code is as follow for simple testing. Can anyone help me to fix what have I done wrong?
#include <cstdlib>
#include <unordered_map>
#include <iostream>
using namespace std;
class MyClass{
public:
MyClass(int id){m_id = id;};
void PrintThis(){cout << " This is test " << endl;};
int m_id;
};
class Test{
public:
unordered_map<MyClass* mc, int test> mapTest;
};
int main(){
MyClass* mc1 = new MyClass(1);
MyClass* mc2 = new MyClass(2);
Test* tt1 = new Test();
tt1->mapTest.insert(make_pair<MyClass*, int>(mc1, 10));
tt1->mapTest.insert(make_pair<MyClass*, int>(mc2, 20));
auto search = tt1->find(mc1);
if(search != tt1->end()) {
search->first->PrintThis();
}else{
cout << "not Found " << endl;
}
}
Error message is as follow
./main.cpp:17:44: error: wrong number of template arguments (1, should be 5)
unordered_map<MyClass* mc, int test> mapTest;
^
In file included from /usr/include/c++/4.8/unordered_map:48:0,
from ./main.cpp:2:
/usr/include/c++/4.8/bits/unordered_map.h:97:11: error: provided for 'template<class _Key, class _Tp, class _Hash, class _Pred, class _Alloc> class std::unordered_map'
class unordered_map : __check_copy_constructible<_Alloc>
^
./main.cpp: In function 'int main()':
./main.cpp:26:18: error: request for member 'insert' in 'tt1->Test::mapTest', which is of non-class type 'int'
tt1->mapTest.insert(make_pair<MyClass*, int>(mc1, 10));
I think I can manage line 26 error, if line 17 gets fixed...
Thanks in advance!
I tried your code and found 3 problems:
Declaration of map: it should read std::unordered_map<MyClass*, int>
call of undefined functions (tt1->find/tt1->end, should read tt1->testMap.XXX)
Call of make_pair doesn't require template arguments. The compiler will infer them. This actually causes a problem, as the compiler tries to call make_pair(MyClass *&&, int &&). If I omit the template arguments, it works (make_pair(mc1, 10))
As for point 3:
make_pair is declared as follows in C++11 (C++14 just adds constexpr) (cppreference):
template< class T1, class T2 >
std::pair<V1,V2> make_pair( T1&& t, T2&& u );
For template argument deduction, the follwing rule applies (cf. cppreference)
4) If P is an rvalue reference to a cv-unqualified template parameter (so-called "forwarding reference"), and the corresponding function call argument is an lvalue, the type lvalue reference to A is used in place of A for deduction (Note: this is the basis for the action of std::forward)
(emphasis mine)
So the compiler will infer:
std::make_pair<MyClass *&, int>(MyClass *&, int &&);
where MyClass *& can bind to your actual argument.
If you directly specify the template types, the compiler will stick to
std::make_pair<MyClass *, int>(MyClass *&&, int &&).
As your argument is a lvalue, it cannot be converted to a rvalue-reference, and compilation fails
Your declaration of unordered_map<MyClass* mc, int test> mapTest; is invalid syntax. It should be unordered_map<MyClass*, int> mapTest.
Also remove the template parameters from your make_pair calls and change tt1->find to ttl->mapTest.find() and ttl->end() to ttl->mapTest.end().

Having a function only accept non-const lvalues

I have a function which sorts two vectors with the first of them as ordering criterion. Its signature is
template<typename A, typename B>
void sort(A&& X, B&& Y)
{
..
}
The problem is that universal references would allow nonsense cases like
sort(vector<int>{ 2,1,3 }, vector<int>{ 3,1,2 });
where an rvalue will be destroyed afterwards (nonsense).
Asking explicitly for a lvalue doesn't work since
template<typename A, typename B>
void sort(A& X, B& Y) ... // (*)
sort(vector<int>{2,1,3}, vector<int>{3,1,2});
for some reason the above compiles (I thought only const lvalues were allowed to bind to rvalues and to prolong their lifetime?).
If I add const to the lvalue reference then the function will no longer be able to modify the vectors and sort them.
My questions are:
1) Why in the example marked with // (*) can I bind a rvalue to a lvalue that is not even const ? Why instead something like int& r = 20; isn't allowed? What's the difference?
2) How can I solve my issue i.e. having the function accept only lvalues and not rvalue temporaries? (If it's possible, of course)
Obviously I'm allowed to use any C++ version available
The answer is: your compiler is wrong.
Check on gcc or clang or similar and you'll get something like this:
prog.cpp: In function 'int main()': prog.cpp:9:45: error: invalid
initialization of non-const reference of type 'std::vector&' from
an rvalue of type 'std::vector' sort(vector{2,1,3},
vector{3,1,2});
^ prog.cpp:6:6: note: initializing argument 1 of 'void sort(A&, B&) [with A =
std::vector; B = std::vector]' void sort(A& X, B& Y) { }
You can use the /Za compiler option to turn this into an error:
error C2664: 'void sort<std::vector<int,std::allocator<_Ty>>,std::vector<_Ty,std::allocator<_Ty>>>(A &,B &)' : cannot convert argument 1
from 'std::vector<int,std::allocator<_Ty>>' to 'std::vector<int,std::allocator<_Ty>> &'
with
[
_Ty=int
, A=std::vector<int,std::allocator<int>>
, B=std::vector<int,std::allocator<int>>
]
and
[
_Ty=int
]
and
[
_Ty=int
]
A non-const reference may only be bound to an lvalue
Note that /Za has had quite some issues in the past and even nowadays still breaks <windows.h>, so you cannot use it for all compilation units anyway. In a 2012 posting titled "MSVC /Za considered harmful", Microsoft senior engineer Stephan T. Lavavej even recommends not using the flag, but you should also have a look at the comments at STL Fixes In VS 2015, Part 2, where he says:
We've definitely had meetings about the /Za and /Zc conformance
options. We ultimately want to get to a point where VC is conformant
by default, without having to request extra options, so that becomes
the most-used and most-tested path. As you can see in the post, I've
been working towards this in the STL by removing non-Standard
machinery whenever possible.
So, chances are this will be a compilation error by default in some future version of MSVC.
One other thing: The C++ standard does not distinguish between errors and warnings, it only talks about "diagnostic messages". Which means that MSVC actually is conforming as soon it produces a warning.
As noted by other answers, the compiler is wrong.
Without having to change compiler of compiler options:
struct sfinae_helper {};
template<bool b>
using sfinae = typename std::enable_if<b, sfinae_helper>::type*;
// sfinae_helper, because standard is dumb: void*s are illegal here
template<class A, class B,
sfinae<!std::is_const<A>::value&&!std::is_const<B>::value> = nullptr
>
void sort(A& X, B& Y) ... // (*)
sort(vector<int>{2,1,3}, vector<int>{3,1,2});
will fail to compile in MSVC2013 as well, and should be compliant in compliant compilers.
Note that while deducing A and B as const X is not legal under the standard, explicitly passing const X as A or B is.
A final approach is:
template<typename A, typename B>
void sort(A& X, B& Y) ... // (*)
template<typename A, typename B>
void sort(A&& X, B&& Y) = delete;
where we generate an explicitly deleted one that should be preferred to the A&, B& one. I do not know if MSVC properly picks the perfectly forwarded one in that case, but I hope so.
As an answer to the X problem you're trying to solve rather than the Y problem you asked... the right answer is that you shouldn't do what you're trying to do. Being unable to imagine how something can be useful is not an adequate reason to go out of your way to prevent people from being able to do it.
And, in fact, I don't even have to suggest this in the abstract: here are two concrete examples where accepting a temporary object would be useful.
You might only care about one of the two objects:
interesting_container A;
// fill A
sort(an_ordering_criterion(), A);
The containers aren't 'self-contained'; e.g. a container that provides a view into another one:
vector<int> A, B;
// fill A and B
sort(make_subsequence(A, 1, 10), make_subsequence(B, 5, 14));
You can explicitly delete undesired overloadings of sort function:
#include <iostream>
#include <vector>
#include <cstdlib>
template< typename X, typename Y >
void
sort(X &, Y &)
{
static_assert(!std::is_const< X >{});
static_assert(!std::is_const< Y >{});
}
template< typename X, typename Y >
int
sort(X const &, Y &) = delete;
template< typename X, typename Y >
int
sort(X &, Y const &) = delete;
template< typename X, typename Y >
int
sort(X const &, Y const &) = delete;
int
main()
{
std::vector< int > v{1, 3, 5};
std::vector< int > const c{2, 4, 6};
::sort(v, v); // valid
{ // has been explicitly deleted
//::sort(v, c);
//::sort(c, v);
//::sort(c, c);
}
{ // not viable: expects an l-value for 1st argument
//::sort(std::move(v), v);
//::sort(std::move(v), c);
//::sort(std::move(c), v);
//::sort(std::move(c), c);
}
{ // not viable: expects an l-value for 2nd argument
//::sort(v, std::move(v));
//::sort(v, std::move(c));
//::sort(c, std::move(v));
//::sort(c, std::move(c));
}
{ // not viable: expects an l-value for 1st or 2nd argument
//::sort(std::move(v), std::move(v));
//::sort(std::move(v), std::move(c));
//::sort(std::move(c), std::move(v));
//::sort(std::move(c), std::move(c));
}
return EXIT_SUCCESS;
}

C++ Operator overloading - casting from class

While porting Windows code to Linux, I encountered the following error message with GCC 4.2.3. (Yes, I'm aware that it's a slight old version, but I can't easily upgrade.)
main.cpp:16: error: call of overloaded ‘list(MyClass&)’ is ambiguous
/usr/include/c++/4.2/bits/stl_list.h:495: note: candidates are: std::list<_Tp, _Alloc>::list(const std::list<_Tp, _Alloc>&) [with _Tp = unsigned char, _Alloc = std::allocator<unsigned char>]
/usr/include/c++/4.2/bits/stl_list.h:484: note: std::list<_Tp, _Alloc>::list(size_t, const _Tp&, const _Alloc&) [with _Tp = unsigned char, _Alloc = std::allocator<unsigned char>]
I'm using the following code to generate this error.
#include <list>
class MyClass
{
public:
MyClass(){}
operator std::list<unsigned char>() const { std::list<unsigned char> a; return a; }
operator unsigned char() const { unsigned char a; return a; }
};
int main()
{
MyClass a;
std::list<unsigned char> b = (std::list<unsigned char>)a;
return 0;
}
Has anyone experienced this error? More importantly, how to get around it? (It's possible to completely avoid the overload, sure, by using functions such as GetChar(), GetList() etc, but I'd like to avoid that.)
(By the way, removing "operator unsigned char()" removes the error.)
The ambiguity comes from the interpretation of the cast-expression.
When choosing the conversion, the compiler first considers a static_cast style cast and considers how to resolve an initialization which looks like this:
std::list<unsigned_char> tmp( a );
This construction is ambiguous as a has a user-defined conversion to a std::list<unsigned char> and to an unsigned char and std::list<unsigned char> has both a constructor which takes a const std::list<unsigned char>& and a constructor which takes size_t (to which an unsigned char can be promoted).
When casting to a const std::list<unsigned_char>&, this initialization is considered:
const std::list<unsigned_char>& tmp( a );
In this case, when the user-defined conversion to std::list<unsigned_char> is chosen, the new reference can bind directly to the result of the conversion. If the user-defined conversion to unsigned char where chosen a temporary object of type std::list<unsigned char> would have to be created and this makes this option a worse conversion sequence than the former option.
I've simplified your example to the following:
typedef unsigned int size_t;
template <typename T>
class List
{
public:
typedef size_t size_type;
List (List const &);
List (size_type i, T const & = T());
};
typedef List<unsigned char> UCList;
class MyClass
{
public:
operator UCList const () const;
operator unsigned char () const;
};
void foo ()
{
MyClass mc;
(UCList)mc;
}
The first point, is that the standard defines that the C-style cast should use the more appropriate C++ style cast, and in this case that's static_cast. So the above cast is equivalent to:
static_cast<UCList> (mc);
The definition of static_cast says:
An expression e can be explicitly converted to a type T using a static_cast of the form
static_cast<T>(e) if the declaration "T t(e);" is well-formed, for some invented temporary variable
t (8.5)
So the semantics for the cast are the same as for:
UCList tmp (mc);
From 13.3.1.3 we get the set of candidate constructors that we can use in UCList:
UCList (UCList const &) #1
UCList (size_type, T const & = T()); #2
What happens next is two separate overload resolution steps, one for each conversion operator.
Converting to #1: With a target type of UCList const &, overload resolution selects between the following conversion operators.: "operator UCList const ()" and "operator unsigned char ()". Using unsigned char would require an additional user conversion and so is not a viable function for this overload step. Therefore overload resolution succeeds and will use operator UCList const ().
Converting to #2: With a target type of size_t. The default argument does not take part in overload resolution. Overload resolution again selects between the conversion operators: "operator UCList const ()" and "operator unsigned char ()". This time there is no conversion from UCList to unsigned int and so that is not a viable function. An unsigned char can be promoted to size_t and so this time overload resolution succeeds and will use "operator UCList const ()".
But, now back at the top level there are two separate and independent overload resolution steps that have successfully converted from mc to UCList. The result is therefore ambiguous.
To explain that last point, this example is different to the normal overload resolution case. Normally there is a 1:n relationship between argument and parameter types:
void foo (char);
void foo (short);
void foo (int);
void bar() {
int i;
foo (i);
}
Here there is i=>char, i=>short and i=>int. These are compared by overload resolution and the int overload would be selected.
In the above case we have an m:n relationship. The standard outlines the rules to select for each individual argument and all of the 'n' parameters, but that's where it ends, it does not specify how we should decide between using the different 'm' arguments.
Hope this makes some sense!
UPDATE:
The two kinds of initialization syntax here are:
UCList t1 (mc);
UCList t2 = mc;
't1' is a direct initialiation (13.3.1.3) and all constructors are included in the overload set. This is almost like having more than one user defined conversion. There are the set of constructors and the set of conversion operators. (ie. m:n).
In the case of 't2' the syntax uses copy-initialization (13.3.1.4) and the rules different:
Under the conditions specified in 8.5, as part of a copy-initialization of an object of class type, a userdefined
conversion can be invoked to convert an initializer expression to the type of the object being initialized.
Overload resolution is used to select the user-defined conversion to be invoked
In this case there is just one to type, UCList, and so there is only the set of conversion operator overloads to consider, ie. we do not consider the other constructors of UCList.
It compiles properly if you remove the cast, and I've checked that the operator std::list is being executed.
int main()
{
MyClass a;
std::list<unsigned char> b = a;
return 0;
}
Or if you cast it to a const reference.
int main()
{
MyClass a;
std::list<unsigned char> b = (const std::list<unsigned char>&)a;
return 0;
}