I am puzzled by the following piece of code:
#include <Eigen/Dense>
#include <vector>
class Foo {};
void f(Eigen::MatrixXd const &) {}
void f(std::vector<Eigen::MatrixXd> const &) {}
void g(Foo const &) {}
void g(std::vector<Foo> const &) {}
int main()
{
Foo a, b, c;
Eigen::MatrixXd x, y, z;
// f({x, y}); ambiguity, why?!
f({x, y, z}); // ok
g({a,b}); // ok
g({a,b,c}); // ok
}
If I un-comment the 3-rd code line in main(), I get an ambiguous call error,
/Users/vlad/so.cpp: In function 'int main()':
/Users/vlad/so.cpp:17:13: error: call of overloaded 'f(<brace-enclosed initializer list>)' is ambiguous
f({x, y}); //ambiguity, why?!
^
/Users/vlad/so.cpp:17:13: note: candidates are:
/Users/vlad/so.cpp:6:6: note: void f(const MatrixXd&)
void f(Eigen::MatrixXd const &) {}
^
/Users/vlad/so.cpp:7:6: note: void f(const std::vector<Eigen::Matrix<double, -1, -1> >&)
void f(std::vector<Eigen::MatrixXd> const &) {}
Calling it with 3 items in the init list works.
However, if instead of using Eigen matrices, I use my own class Foo (see the function g), everything works fine. I have absolutely no clue why the commented line is ambiguous when using Eigen. Any ideas?
PS: If I overload f so that it takes a std::initializer_list<Eigen::MatrixXd>, then the problem disappears, no more ambiguous call.
The error is most likely being caused by this constructor template.
template<typename T0, typename T1>
EIGEN_DEVICE_FUNC
EIGEN_STRONG_INLINE Matrix(const T0& x, const T1& y)
{ ... }
Both that constructor and vector's initializer_list constructor are equally good matches in the function call f({x, y});, leading to the ambiguity error.
Here's a made up example with similar constructors and function calls that leads to an ambiguity error as well.
Related
Surprisingly (embarrassingly?) I cannot get the syntax of the static_const of a const member function right. In short (details below) if the member function is not marked const I use:
static_cast<std::vector<double> (mymodule::Foo::*)(const std::vector<double>&)>(&mymodule::Foo::bar)
but marking the member function Foo::bar(...) const the compiler does not know what to do:
error: address of overloaded function 'bar' cannot be static_cast to type 'std::vector<double> (mymodule::Foo::*)(const std::vector<double> &)'
Where should I put the function's constness?
Details
I'm trying to create Python binding for the following module:
namespace mymodule {
class Foo
{
public:
Foo() = default;
template <class T>
T bar(const T& a) const
{
T ret = a;
for (auto& i : ret) {
i *= 2.0;
}
return ret;
}
template <class T>
T bar(const T& a, double f) const
{
T ret = a;
for (auto& i : ret) {
i *= f;
}
return ret;
}
};
} // namespace mymodule
whereby I write the Python bindings with pybind11:
#include <pybind11/pybind11.h>
namespace py = pybind11;
PYBIND11_MODULE(example, m)
{
py::class_<mymodule::Foo>(m, "Foo")
.def(py::init<>())
.def("bar",
static_cast<std::vector<double> (mymodule::Foo::*)(const std::vector<double>&)>(&mymodule::Foo::bar),
py::arg("a"))
.def("bar",
static_cast<std::vector<double> (mymodule::Foo::*)(const std::vector<double>&, double)>(&mymodule::Foo::bar),
py::arg("a"),
py::arg("f"));
}
which fails to compile:
.../example.cpp:54:14: error: address of overloaded function 'bar' cannot be static_cast to type 'std::vector<double> (mymodule::Foo::*)(const std::vector<double> &)'
static_cast<std::vector<double> (mymodule::Foo::*)(const std::vector<double>&)>(&mymodule::Foo::bar),
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.../example.cpp:19:7: note: candidate function template
T bar(const T& a) const
^
.../example.cpp:29:7: note: candidate function template
T bar(const T& a, double f) const
^
.../example.cpp:58:14: error: address of overloaded function 'bar' cannot be static_cast to type 'std::vector<double> (mymodule::Foo::*)(const std::vector<double> &, double)'
static_cast<std::vector<double> (mymodule::Foo::*)(const std::vector<double>&, double)>(&mymodule::Foo::bar),
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.../example.cpp:19:7: note: candidate function template
T bar(const T& a) const
^
.../example.cpp:29:7: note: candidate function template
T bar(const T& a, double f) const
^
2 errors generated.
You should add const at last as:
static_cast<std::vector<double> (mymodule::Foo::*)(const std::vector<double>&) const>(&mymodule::Foo::bar),
// ^^^^^
The program below generates a compiler error:
MSVC: error C2782: 'double dot(const V &,const V &)': template parameter 'V' is ambiguous
GCC: deduced conflicting types for parameter 'const V' ('Matrix<3, 1>' and 'UnitVector')
I had thought that it would not have this problem because the constructor UnitVector(Vector) is marked explicit, and therefore the arguments (of the call to dot()) can only resolve as Vector with implicit conversions. Can you tell me what I am misunderstanding? Does the compiler consider explicit constructors as implicit conversions when resolving template parameters?
template<int M, int N>
struct Matrix {
};
using Vector = Matrix<3,1>;
struct UnitVector : Vector{
UnitVector(){}
explicit UnitVector(const Vector& v)
{}
operator const Vector&(){
return *static_cast<const Vector*>(this);
}
};
template<typename V>
double dot(const V& a, const V& b){
return 0.0;
}
int main()
{
dot(Vector(),UnitVector());
}
No, that does not work. But actually, you don't need the template
double dot(const Vector &, const Vector &) {...}
works. You don't even need the conversion operator defined in UnitVector. Child to base conversions are done implicitly.
If you want to generally take two types that are implicitly convertible to a common type, the following should work (untested)
template<class U>
double dot_impl(const U&, const U&) {...}
template<class U, class V>
auto dot(const U &u, const V &v) {
return dot_impl<std::common_type_t<U, V>>(u, v);
}
Since the template parameter is explicit, the implicit conversions to thw common type of those two are done in the call, so everything works nice. I moved the original dot to dot_impl, since otherwise we would call dot with one template parameter, which could still be ambiguous.
What happens if there is a
Foo::test(Foo::A &a, Bar::B &b, C &c); and a
Bar::test(Foo::A &a, Bar::B &b, C &c);.
Are the namespaces of the arguments considered in-order by the compiler (the first argument taking precedence for the argument-dependent-lookup), or is this considered to be ambiguous?
It will be ambiguous. The overload set contains two equally valid overloads:
namespace Bar
{
struct B;
}
namespace Foo
{
struct A{};
void test(A& , Bar::B&, int){}
}
namespace Bar
{
struct B{};
void test(Foo::A& , B&, int){}
}
int main() {
Foo::A a; Bar::B b;
test (a, b, 0);
return 0;
}
results on gcc in:
prog.cpp: In function 'int main()':
prog.cpp:21:15: error: call of overloaded 'test(Foo::A&, Bar::B&, int)' is ambiguous test (a, b, 0);
^ prog.cpp:10:7: note: candidate: void Foo::test(Foo::A&, Bar::B&, int) void test(A& , Bar::B&, int){}
^ prog.cpp:16:7: note: candidate: void Bar::test(Foo::A&, Bar::B&, int) void test(Foo::A& , B&, int){}
According to the section 3.4.2 of the standard
For each argument type T in the function call, there is a set of zero or more associated namespaces
So both namespaces Foo and Bar will be in the set of associated namespace. As function test is found in both, it will be ambiguous.
I am experimenting with the perfect forwarding feature of C++11. Gnu g++ compiler reports an ambiguity issue of function-parameter binding (the error is shown after the source code below). My question is why is it so, as following the function-parameter binding process I don't see the ambiguity. My reasoning is as follows: call to tf(a) in main() binds to tf(int&) since a is an lvalue. Then function tf forwards the lvalue reference int& a to function g hence the function void g(int &a) should be uniquely invoked. Thus I do not see the reason for ambiguity. The error disappears when the overloaded function g(int a) is removed from the code. This is strange as g(int a) cannot be a candidate for binding with int &a.
Here is my code:
void g(int &&a)
{
a+=30;
}
void g(int &a)
{
a+=10;
}
void g(int a) //existence of this function originates the ambiguity issue
{
a+=20;
}
template<typename T>
void tf(T&& a)
{
g(forward<T>(a));;
}
int main()
{
int a=5;
tf(a);
cout<<a<<endl;
}
Compilation g++ -std=c++11 perfectForwarding.cpp reports the following errors:
perfectForwarding.cpp: In instantiation of ‘void tf(T&&) [with T = int&]’:
perfectForwarding.cpp:35:7: required from here
perfectForwarding.cpp:24:3: error: call of overloaded ‘g(int&)’ is ambiguous
perfectForwarding.cpp:24:3: note: candidates are:
perfectForwarding.cpp:6:6: note: void g(int&&) <near match>
perfectForwarding.cpp:6:6: note: no known conversion for argument 1 from ‘int’ to ‘int&&’
perfectForwarding.cpp:11:6: note: void g(int&)
perfectForwarding.cpp:16:6: note: void g(int)
This is strange as g(int a) cannot be a candidate for binding with int &a.
That's not true. If you remove the g(int&) overload then g(int) will get called. When both are declared it is ambiguous, because both are viable candidates and require no conversions.
Adding on top of Jonathan Wakely's answer.
First of all, the issue has nothing to do with perfect forwarding and we can remove tf from the picture.
For the time being consider just this code:
void g(int) {}
int main() {
int a = 5; // a is an lvalue
g(a); // ok
g(std::move(a)); // std::move(a) casts a to an rvalue and this call is also ok
}
This illustrates that a function that takes a parameter by value can take both lvalues and rvalues.
Now suppose we add
void g(int &) {}
then the first call, g(a);, becomes ambigous because g(int &) can take non-const lvalues and nothing else. The second call, g(std::move(a)) is still ok and still calls g(int) because g(int &) can't take rvalues.
Now replace g(int &) with g(int &&). The latter function can take non-const rvalues only. Hence the call g(a) is ok and calls g(int). However, g(std::move(a)) is now ambiguous.
At this point it becomes obvious that if we have the three overloads together, then the two calls become ambiguous. Actually, there's no reason for having the three overloads. Depending on the type T, most often we have either
g(T) or
g(T&) or
g(const T&) or
g(const T&) and g(T&&).
code:
#include<iostream>
using namespace std;
template<class T, int N> class point {
T coordinate[N];
public:
point(const point<T,N>&);
const double& operator[](int i) const {
return coordinate[i];
}
};
template<class T, int N> point<T,N>::point(const point<T,N>&p)
{
for(int i=0;i<N;i++)
coordinate[i]=p.coordinate[i];
};
int main() {
point<int,2> P2;
point<double,3> P3;
cout<<P2[0]<<P3[1];
return 0;
}
output:
prog.cpp: In function ‘int main()’:
prog.cpp:17: error: no matching function for call to ‘point<int, 2>::point()’
prog.cpp:11: note: candidates are: point<T, N>::point(const point<T, N>&) [with T =
int, int N = 2]
prog.cpp:18: error: no matching function for call to ‘point<double, 3>::point()’
prog.cpp:11: note: candidates are: point<T, N>::point(const point<T, N>&) [with T =
double, int N = 3]
prog.cpp: In member function ‘const double& point<T, N>::operator[](int) const [with
T = int, int N = 2]’:
prog.cpp:19: instantiated from here
prog.cpp:8: warning: returning reference to temporary
Please help me sort out the faults.
The compiler-generated default constructor is not being provided because you have created a constructor of your own. Therefore when you create P2 with no arguments to its constructor, you need to define a default constructor for it to compile.
When you declare a variable something like,
point<int,2> P2;
It uses default constructor; it can be used in 2 scenarios:
You haven't declared ANY
constructor in your class body. Thus
compiler will generate a default one
automatically and you can use it.
You declare/define a default
constructor explicitly (be it empty,
if you don't do anything)
Since here you don't do anything: just declare an empty default constructor:
template<class T, int N> class point {
//...
public:
point() {} // <-- default constructor
};
This will clear your errors.
Also there is an Important Warning:
prog.cpp:8: warning: returning reference to temporary
That is because of your operator [].
Change the line,
const double& operator[](int i) const
To,
const T& operator[](int i) const // for <int, N> you should return 'int' not 'double'
The problem is that with these two lines
point<int,2> P2;
point<double,3> P3;
you are attempting to create two 'point' object via the default parameterless constructor.
However, this constructor is not automatically generated unless you do not specify any others. Implementing the default constructor will solve you problem