I'm writing some funtions that are supposed to take Eigen::Array as an input.
The Arrays are of constant size, but the size is a template parameter and should be deducted from the input.
When compiling with MSVS, i have to supply the size to the function or it will result in an error.
#include <Eigen/Core>
template<unsigned short t_iSize>
void foo(const Eigen::Array<unsigned short, t_iSize, 1>&)
{
}
int main()
{
Eigen::Array<unsigned short, 3, 1> test;
// foo(test); // Compiler errors C2672 and C2784
foo<3>(test); // Giving the size solves the errors
}
The size should be possible to be deducted from the variable test, but it seems to fail when computing the template arguments 4 and 5 for the Array.
Error C2672: "foo": no matching overloaded function found.
Error C2784: "void foo(const Eigen::Array< unsigned short,t_iSize,1,|_Rows==&&?:&&_Rows!=?:,_Rows,1> &)": could not deduce template argument for "const Eigen::Array< unsigned short,t_iSize,1,|_Rows==&&?:&&_Rows!=?:,_Rows,1> &" from "Eigen::Array< unsigned short,3,1,0,3,1>".
Is it possible to avoid this problem when taking the Eigen::Array, or do I need to take Eigen::ArrayBase as a function parameter? I would like to avoid that, as it obscures the fact that the function only takes this specific type of arrays.
Edit:
As Jarod42 noted, the template parameter should be of type int.
Visual Studio can compile the code with this mistake.
It fails in deducing the parameter _Rows, though, where other compilers can do so.
Here you can see the problem I ran into.
Declaration of Eigen::Array is
template<typename _Scalar, int _Rows, int _Cols, int _Options, int _MaxRows, int _MaxCols>
class Eigen::Array;
Your function uses wrong type for row, unsigned short should be int.
template <int t_iSize>
void foo(const Eigen::Array<unsigned short, t_iSize, 1>&)
{
// ...
}
Demo
As workaround for your issue with Msvc, you might do:
apply the default value manually:
template <int t_iSize>
void bar(const Eigen::Array<unsigned short, t_iSize, 1, 0, t_iSize, 1>&) {
// ...
}
or add the extra template (and so the code is even more generic):
template <int t_iSize, int Options, int MaxRows, int MaxCols>
void foo(const Eigen::Array<unsigned short, t_iSize, 1, Options, MaxRows, MaxCols>&) {
// ...
}
Demo
Related
I am trying to write a function that takes an Eigen::Vector<T, dim> as a parameter. However, the following example fails to compile:
#include <Eigen/Core>
template<class F, typename T, int dim>
void bar(F&& func, const Eigen::Vector<T, dim>& arg1) {
}
template<typename T, int dim>
void foo(const Eigen::Vector<T, dim>& a) {
return bar([] {}, a);
}
int main() {
Eigen::Vector<float, 3> v1{ 1.f,2.f,3.f };
foo(v1);
return 0;
}
This, under Visual Studio 2019, gives me the following error:
1>main.cpp(9,10): error C2672: 'bar': no matching overloaded function found
1>main.cpp(14): message : see reference to function template instantiation 'void foo<float,3>(const Eigen::Matrix<float,3,1,0,3,1> &)' being compiled
1>main.cpp(9,1): error C2784: 'void bar(F &&,const Eigen::Matrix<T,dim,1,|_Rows==&&?:&&_Rows!=?:,_Rows,1> &)': could not deduce template argument for 'const Eigen::Matrix<T,dim,1,|_Rows==&&?:&&_Rows!=?:,_Rows,1> &' from 'const Eigen::Matrix<float,3,1,0,3,1>'
1>main.cpp(4): message : see declaration of 'bar'
My questions:
What is this weird |_Rows==&&?:&&_Rows!=?: in the error message?
What can I do to make the above code compile?
The bar function should have T and dim availabe. I cannot just take const AnyType& arg1, because the actual implementation of bar depends on compile-time known values T and dim.
I have seen https://eigen.tuxfamily.org/dox/TopicFunctionTakingEigenTypes.html. I think I understand what they are saying, but I am not sure if it applies here. I am taking an actual Eigen::Vector as an argument, not an expression.
If there was an expression it would be fine for me, to have it materialized.
Nevertheless, if I try to follow their instruction and just use ArrayBase<Derived>, I lose the compile-time information about T and dim.
This indeed looks like an MSVC issue, it compiles fine with gcc >= 4.7, and clang >= 3.5: https://godbolt.org/z/kqoHyO
One possible workaround would be to explicitly write out what Eigen::Vector expands to:
template<class F, typename T, int dim>
void bar(F&& func, const Eigen::Matrix<T, dim, 1, 0, dim, 1>& arg1) {
}
https://godbolt.org/z/vlvSDP
The weird |_Rows==&&?:&&_Rows!=?: looks like MSVC mangled the default value of the Options template parameter:
AutoAlign |
( (_Rows==1 && _Cols!=1) ? Eigen::RowMajor
: (_Cols==1 && _Rows!=1) ? Eigen::ColMajor
: EIGEN_DEFAULT_MATRIX_STORAGE_ORDER_OPTION ),
If you want to get to the bottom of this, you should file a bug-report to the MSVC maintainers, maybe using a simplified example like this: https://godbolt.org/z/U_0Sh7 (probably it's possible to reduce this even more).
Is it valid to have variations of the same template function that differ by the type of a non-type member?
template<typename T, unsigned int V>
void f(unsigned int& v) { v = V; }
template<typename T, bool B>
void f(bool& b) { b = B; }
The intent is that one be able to call
unsigned int meaningOfLife;
f<sometype, 42>(meaningOfLife);
bool areYouAlive;
f<sometype, true>(areYouAlive);
clang and gcc are silent but MSVC reports
warning C4305: 'specialization': truncation from 'int' to 'bool'
I'd like to avoid requiring specification of the constant type:
f<sometype, bool, true>
and want to ensure that the constant value and the destination value match.
---- mcve ----
#include <iostream>
template<unsigned int V>
void f(unsigned int& v) { v = V; }
template<bool B>
void f(bool& b) { b = B; }
int main()
{
unsigned int u { 0 };
bool b { false };
f<42>(u);
f<true>(b);
std::cout << u << b;
}
Rextester example: http://rextester.com/VIGNP16100
Warning(s):
source_file.cpp(14): warning C4305: 'specialization': truncation from 'int' to 'bool'
/LIBPATH:C:\boost_1_60_0\stage\lib
Microsoft (R) C/C++ Optimizing Compiler Version 19.00.23506 for x64
421
Short answer: The code is OK and MSVC emits a bogus warning.
MSVC and g++ both have bugs in non-type template argument matching but they do select the right one for your particular example.
Long answer: It is OK to have overloaded function templates with non-type template parameters.
However the matching of a template-argument to a template declaration does not work as might be expected (by me anyway). ALL matching templates are entered into overload resolution. It does not, at any stage, prefer an "exact match".
According to C++17 [temp.arg.nontype/]2, converted constant expressions are allowed. That means, for example:
42 matches int and unsigned int.
42u matches int and unsigned int.
1u matches unsigned int, int and bool.
Note that a converted constant expression cannot contain a narrowing conversion, and int to bool is narrowing unless the value is a constant expression of value 0 or 1. So 42 does not match bool. (Ref: C++17 [expr.const]/4).
If we had the following setup:
template<unsigned int V> void g() {}
template<bool B> void g() {}
then the correct behaviour is:
g<42>() calls g<unsigned int>.
g<1>() is ambiguous.
g<1u>() is ambiguous.
MSVC 2017 and g++ 7,8 all incorrectly allow g<42> to match g<bool>, and report g<42> as ambiguous.
MSVC emits the warning that you see whilst generating the invalid match; g++ gives no diagnostic at all. If we remove the unsigned int overload then g++ silently accepts the invalid code with no diagnostic.
In your code there is a non-const lvalue reference parameter:
template<unsigned int V> void h(unsigned int&) {}
template<bool B> void h(bool&) {}
This makes a difference because overload resolution can make a selection based on the function argument. For the call:
unsigned int m;
h<1u>(m);
then both overloads of h are entered into overload resolution, however then h<unsigned int> wins because h<bool>(m) would be invalid.
As discussed above, The call h<42>(m); wins at the first stage because this cannot match h<bool>; but in MSVC++ (and g++) it incorrectly allows h<bool> to go through at this stage, but prunes it later as for the h<1u> case.
I have following implementation:
#include <cstddef>
template<typename Data, size_t Size>
class Demo
{
public:
Demo();
private:
Data data[Size];
};
void f(Demo<int, size_t>& demoObj)
{
}
int main()
{
Demo<int, 100> demoObj;
}
I get the following error when I compile:
g++ -std=c++11 temp.cpp
temp.cpp:13:24: error: type/value mismatch at argument 2 in template parameter list for ‘template<class Data, long unsigned int Size> class Demo’
void f(Demo<int, size_t>& demoObj)
^
temp.cpp:13:24: note: expected a constant of type ‘long unsigned int’, got ‘size_t {aka long unsigned int}’
The error is not making sense to me. Please help me understand it. Also, how do I pass demoObj to function f? I mean how o write the definition of f.
Size is a non-type parameter, so it requires a non-type argument:
void f(Demo<int, 100>& demoObj);
// ^^^
If you want to be able to pass in any kind of Demo you can define f as a template function.
template<typename Data, size_t Size>
void f(Demo<Data, Size>& demoObj)
{
// ...
}
Consider this code:
constexpr int TEN = 10;
template < const int& >
struct Object { };
template < const int& II >
void test(Object<II>) { }
Then the calls:
test<TEN>(Object<TEN>{}); // passes
test(Object<TEN>{}); // FAILS
The second call fails to compile with error message:
error: no matching function for call to ‘test(Object<TEN>)
note: candidate: template<const int& II> void test(Object<II>)
note: template argument deduction/substitution failed:
note: couldn't deduce template parameter ‘II’
The question is why? Is it according to the standard?
And the more important question is: how can I workaround this? That is: how can I help the compiler to deduce the const int& template parameter?
In the real code instead of int I have more complex literal type, so I do need the const&. Thus I can't just "use int instead of const int&".
I am using gcc-7.0.1 (the snapshot) and I am getting the same error with options -std=c++11, -std=c++14, -std=c++17.
I have a problem compiling a template using msvc-2010. It works perfectly using gcc 4.6.3.
I have boiled down the code to the essential (it doesn't make sense of course):
//Variant that works
template <typename T, T* Ptr>
void callFun()
{
}
//Traits class (type expands to the same type T* as above)
template <typename T>
class TraitsClass
{
public:
typedef T* type;
};
//Essentially the same as callFun2, only that the
//type of Ptr is expressed indirectly over a traits class
//The usage of this class is not possible, because of the error described below
template <typename T, typename TraitsClass<T>::type Ptr>
void callFun2()
{
}
//Provides a compile constant ptr for this example
void testFun()
{
}
int main()
{
//Works
callFun<void(), &testFun>();
//Fails
callFun2<void(), &testFun>();
//Works
callFun2<void(), 0>();
return 0;
}
The Error:
error C2975: 'Ptr' : invalid template argument for 'callFun2', expected compile-time constant expression
I find it interesting, that it only fails when the second type parameter is being used through a typedef in a Traits class.
g++ compiles this example correctly without warnings, even when using -Wall -Wextra -Werror -pedantic (Except for the unused parameters, of course)
Thank you very much.
Well, I think that the answer is that compilers are not written by gods. Programming standards in the compiler industry are extremely high, MS C++ is a good compiler, but it still contain bugs. I came across the following, that is somehow similar to what you are pointing at:
template <class item_struct>
struct THeapBasedArray
{
void Sort(int (__cdecl *compareFunction)(const item_struct *item1,
const item_struct *item2));
};
struct Item { int x; };
struct ItemPtrsArray : public THeapBasedArray<Item*>
{
static int __cdecl Compare1(const Item **pp1, const Item **pp2);
typedef Item *ItemPtr;
static int __cdecl Compare2(const ItemPtr *pp1, const ItemPtr *pp2);
};
int main()
{
ItemPtrsArray vect;
vect.Sort(ItemPtrsArray::Compare1);
vect.Sort(ItemPtrsArray::Compare2);
}
The first call to Sort fails with:
cpptest1.cxx(21) : error C2664: 'THeapBasedArray::Sort' : cannot convert parameter 1 from 'int (_cdecl *)(const Item **, const Item **)' to 'int (_cdecl *)(const item_struct *, const item_struct *)
while the second call compilers fine. For me this is a bug in a compiler. Sometimes this happens. I guess this is the answer.