I have a template class B that derives from a template class A.
Class A has 3 template parameters (the follow is A.h):
#ifndef A_HEADER
#define A_HEADER
#include "Eigen/Dense"
template<typename T = double, int xsize = Eigen::Dynamic, int ysize = Eigen::Dynamic>
class A {
public:
// Constructor, etc.
...
which we derive from in class B:
#ifndef B_HEADER
#define B_HEADER
#include "A.h"
#include "Eigen/Dense"
template<typename T= double, int ysize = Eigen::Dynamic>
class B : public A<T, 4, ysize> {
private:
Eigen::Matrix<T, ysize, 4> Hj = Eigen::Matrix<T, ysize, 4>::Identity(2, 4);
public:
// Constructor, etc.
...
Then in the main function, I instantiate B like this:
B<float> b(...
which I expect to pick up the template defaults. When I try to compile this in Cygwin G++, I don't get any compile errors. However, with Ubuntu G++, I am getting the errors
/mnt/c/Dropbox/drive/kf/src/b.h:11:68: error: declaration of ‘Eigen::Matrix<T, ysize, 4> B<T, ysize>::ysize’
Eigen::Matrix<T, ysize, 4> Hj = Eigen::Matrix<T, ysize, 4>::Identity(2, 4);
^
/mnt/c/Dropbox/drive/kf/src/b.h:7:38: error: shadows template parm ‘int ysize’
template<typename T = double, int ysize = -1>
^
/mnt/c/Dropbox/drive/kf/src/b.h:11:75: error: expected unqualified-id before numeric constant
Eigen::Matrix<T, ysize, 4> Hj = Eigen::Matrix<T, ysize, 4>::Identity(2, 4);
^
/mnt/c/Dropbox/drive/kf/src/b.h:11:58: error: wrong number of template arguments (1, should be at least 3)
Eigen::Matrix<T, ysize, 4> Hj = Eigen::Matrix<T, ysize, 4>::Identity(2, 4);
^
What is the issue with this pattern?
Note: It doesn't hurt me one bit to move the initialization to the constructor. I just don't understand why the Cygwin G++ compiler is fine with this, but the Ubuntu version isn't.
EDIT: Compiler info obtained from g++ --version
Windows Cygwin G++ : g++ (GCC) 6.4.0
Ubuntu G++ : g++ (Ubuntu 5.4.0-6ubuntu1~16.04.6) 5.4.0 20160609
Related
The following code compiles fine, with no warnings/errors:
#include <bitset>
#include <Eigen/Core>
using Array = Eigen::Array<float, -1, 1, 0, 3, 1>;
struct Tree {
Array array;
std::bitset<8> bitset;
};
auto func(Tree* tree) {
int c = tree->array.rows();
int d = tree->bitset.count();
//assert(c==d);
Array e(c);
for (int k = 0; k < d; ++k) e(k) = k;
return e.sum() / (e + 1);
}
int main() {
Tree tree;
func(&tree);
return 0;
}
Compilation cmd:
g++ -O3 -Wall -I anaconda3/envs/dev/include/eigen3/ test.cpp
However, when I uncomment the assert(), I get a maybe-uninitialized warning:
In file included from anaconda3/envs/dev/include/eigen3/Eigen/Core:253,
from test.cpp:2:
In member function ‘Eigen::internal::scalar_sum_op<LhsScalar, RhsScalar>::result_type Eigen::internal::scalar_sum_op<LhsScalar, RhsScalar>::operator()(const LhsScalar&, const RhsScalar&) const [with LhsScalar = float; RhsScalar = float]’,
inlined from ‘static Eigen::internal::redux_impl<Func, Evaluator, 3, 0>::Scalar Eigen::internal::redux_impl<Func, Evaluator, 3, 0>::run(const Evaluator&, const Func&, const XprType&) [with XprType = Eigen::Array<float, -1, 1, 0, 3, 1>; Func = Eigen::internal::scalar_sum_op<float, float>; Evaluator = Eigen::internal::redux_evaluator<Eigen::Array<float, -1, 1, 0, 3, 1> >]’ at anaconda3/envs/dev/include/eigen3/Eigen/src/Core/Redux.h:278:19,
inlined from ‘typename Eigen::internal::traits<T>::Scalar Eigen::DenseBase<Derived>::redux(const Func&) const [with BinaryOp = Eigen::internal::scalar_sum_op<float, float>; Derived = Eigen::Array<float, -1, 1, 0, 3, 1>]’ at anaconda3/envs/dev/include/eigen3/Eigen/src/Core/Redux.h:418:56,
inlined from ‘typename Eigen::internal::traits<T>::Scalar Eigen::DenseBase<Derived>::sum() const [with Derived = Eigen::Array<float, -1, 1, 0, 3, 1>]’ at anaconda3/envs/dev/include/eigen3/Eigen/src/Core/Redux.h:463:25,
inlined from ‘auto func(Tree*)’ at test.cpp:17:15:
anaconda3/envs/dev/include/eigen3/Eigen/src/Core/functors/BinaryFunctors.h:42:122: warning: ‘((const float*)((char*)&e + offsetof(Eigen::Array, Eigen::Array<float, -1, 1, 0, 3, 1>::<unnamed>.Eigen::PlainObjectBase<Eigen::Array<float, -1, 1, 0, 3, 1> >::m_storage)))[3]’ may be used uninitialized [-Wmaybe-uninitialized]
42 | EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE result_type operator() (const LhsScalar& a, const RhsScalar& b) const { return a + b; }
| ~~^~~
test.cpp: In function ‘auto func(Tree*)’:
test.cpp:15:9: note: ‘e’ declared here
15 | Array e(c);
| ^
My question is this: why does the presence of the assert() affect whether or not gcc reports a maybe-uninitialized warning?
The example I show happens to use the eigen3 library, but I think my question is more general. How can the inclusion of an assert() that merely compares two int values lead to a compiler warning like this?
I want to note that this code is a minimal reproducible example for this assert-dependent-warning behavior. Removing the for-loop inside func(), for instance, makes the warning appear even without the assert. Replacing e.sum() / (e + 1) with a simpler expression like e + 1 makes the warning disappear with the assert. Replacing the std::bitset with something else also makes the warning disappear with the assert.
I am using gcc-11.3.0 and eigen-3.4.0.
This is apparently due to a gcc bug. The bug is present in gcc-12 or lower, and is fixed in gcc-13. A fix might be coming in gcc-12.
Bug report: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=108230
The following snippet compiles using the intel icc 2021.2.0 and clang without any warning. But it does not compile icc 2021.3.0 or gcc 11.1 throwing the error
#include <array>
class Base {
public:
enum Types { Al, Mg };
static constexpr unsigned int P = 2;
static constexpr unsigned int bin_Al = 1;
static constexpr unsigned int bin_Mg = 300;
static constexpr std::array<unsigned int, P> bins{bin_Al, bin_Mg};
Base();
};
template <typename Base::Types pNo>
class Derived : public Base {
public:
Derived();
public:
static std::array<double, bins[pNo]> particle_radii;
};
template <typename Base::Types pNo>
std::array<double, Base::bins[pNo]> Derived<pNo>::particle_radii;
Error:
error: conflicting declaration 'std::array<double, pNo->Base::bins.std::array<unsigned int, 2>::operator[]()> Derived<pNo>::particle_radii'
23 | std::array<double, Base::bins[pNo]> Derived<pNo>::particle_radii;
note: previous declaration as 'std::array<double, pNo->Base::bins.std::array<unsigned int, 2>::operator[]()> Derived<pNo>::particle_radii'
19 | static std::array<double, bins[pNo]> particle_radii;
Here is the godbold example.
If I change the std::array to a POD double array, it compiles fine.
Can somebodt explain me why this is not compiling using some compilers?
To initialize an std::array of an arithmetic type AT at compile-time, I did this:
#include <array>
#include <iostream>
template<typename AT, auto DIM, auto N = 0>
constexpr void assign(std::array<AT, DIM>& arr, AT value)
{
arr[N] = value;
if constexpr (N < std::size(arr) - 1)
assign<AT, DIM, N + 1>(arr, value);
}
template<typename AT, auto DIM>
constexpr std::array<AT, DIM> zero_array()
{
std::array<AT, DIM> result;
assign(result, AT{0});
return result;
}
template<typename Container>
void print(Container&& cont)
{
for (const auto& el : cont)
std::cout << el << " ";
std::cout << std::endl;
}
int main()
{
auto zero10 = zero_array<double, 10>();
print(zero10);
}
I godbolted it, and it seems to me it worked:
However, when I compile it with
g++ -O3 -std=c++2a -Wall -Wpedantic -Wunused-parameter -I /usr/include main.cpp -o main
using g++ (GCC) 8.2.1 20181127, I get a "note"
In file included from main.cpp:1:
main.cpp: In instantiation of ‘constexpr std::array<AT, DIM> zero_array() [with AT = double; auto DIM = 10]’:
main.cpp:30:42: required from here
/usr/include/c++/8.2.1/array:94:12: note: ‘struct std::array<double, 10>’ has no user-provided default constructor
struct array
^~~~~
/usr/include/c++/8.2.1/array:110:56: note: and the implicitly-defined constructor does not initialize ‘double std::array<double, 10>::_M_elems [10]’
typename _AT_Type::_Type _M_elems;
Why is this note there? Can I ignore it? If so, how to get rid of it?
std::array does not have a default constructor. You need to use:
std::array<AT, DIM> result = {};
What's puzzling to me is why you think you need zero_array at all. If you use
std::array<double, 10> a = {};
you get a zero-initialized object.
I am experimenting with Eigen::Tensor, and I do not manage to understand why the most basic example is failing. Why does this piece of code
#include <unsupported/Eigen/CXX11/Tensor>
int main()
{
Eigen::Tensor<int, 2> a(4, 4);
Eigen::Tensor<int, 2> b(4, 4);
a.setRandom();
b.setRandom();
a += b;
return 0;
}
results in error:
In file included from eigen.cpp:2:
In file included from /usr/local/include/eigen3/unsupported/Eigen/CXX11/Tensor:142:
/usr/local/include/eigen3/unsupported/Eigen/CXX11/src/Tensor/TensorStorage.h:39:7: error: class template partial specialization is not
more specialized than the primary template [-Winvalid-partial-specialization]
class TensorStorage<T, FixedDimensions, Options_>
^
/usr/local/include/eigen3/unsupported/Eigen/CXX11/src/Tensor/TensorStorage.h:34:63: note: template is declared here
template<typename T, typename Dimensions, int Options_> class TensorStorage;
Please, update to the default branch (recommended for the Tensor module), or at least to the head of the 3.3 branch.
I want to use automatic differentiation mechanism provided by
CppAD inside Eigen linear algebra. An example type is
Eigen::Matrix< CppAD::AD,-1,-1>. As CppAD::AD is a custom numeric type
the NumTraits for this type have to be provided. CppAD provides
those in the file cppad/example/cppad_eigen.hpp. This makes the
following minimal example compile:
#include <cppad/cppad.hpp>
#include <cppad/example/cppad_eigen.hpp>
int main() {
typedef double Scalar;
typedef CppAD::AD<Scalar> AD;
// independent variable vector
Eigen::Matrix<AD,Eigen::Dynamic,1> x(4);
CppAD::Independent(x);
// dependent variable vector
Eigen::Matrix<AD,Eigen::Dynamic,1> y(4);
Eigen::Matrix<double,Eigen::Dynamic,Eigen::Dynamic> m(4,4);
m.setIdentity();
y = 1. * x;
// THIS DOES NOT WORK
// y = m * x;
CppAD::ADFun<Scalar> fun(x, y);
}
As soon as some more complex expression is used, e.g. the
mentioned
y = m * x;
the code fails to compile:
PATH/Eigen/latest/include/Eigen/src/Core/Product.h:29:116: error:
no type named 'ReturnType' in 'Eigen::ScalarBinaryOpTraits<double, CppAD::AD<double>,
Eigen::internal::scalar_product_op<double, CppAD::AD<double> > >'
...typename ScalarBinaryOpTraits<typename traits<LhsCleaned>::Scalar, typename traits<RhsCleaned>::Scalar>::ReturnType...
If i manually cast the double Matrix to AD, it works. However this
is not a solution because the type promotion is virtually used
everywhere in Eigen.
It looks to me as if the NumTraits provided by CppAD are not sufficient for this case. This is supported by a followup error message:
PATH/Eigen/latest/include/Eigen/src/Core/Product.h:155:5: error:
no type named 'CoeffReturnType' in
'Eigen::internal::dense_product_base<Eigen::Matrix<double, -1, -1, 0, -1, -1>,
Eigen::Matrix<CppAD::AD<double>, -1, 1, 0, -1, 1>, 0, 7>'
EIGEN_DENSE_PUBLIC_INTERFACE(Derived)
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Other use cases lead to error messages like:
PATH/Eigen/src/Core/functors/Binary
Functors.h:78:92: error: no type named ‘ReturnType’ in ‘struct Eigen::ScalarBinaryOpTraits<dou
ble, CppAD::AD<double>, Eigen::internal::scalar_product_op<double, CppAD::AD<double> > >’
Can anybody point me in the correct direction? It is possible that the NumTraits are for old Eigen Versions. I'm using 3.3.2 and CppAD from the
current master branch.
If you want to multiply a Matrix<CppAD::AD<double>, ...> by a Matrix<double, ...> you also need to specialize the corresponding ScalarBinaryOpTraits:
namespace Eigen {
template<typename X, typename BinOp>
struct ScalarBinaryOpTraits<CppAD::AD<X>,X,BinOp>
{
typedef CppAD::AD<X> ReturnType;
};
template<typename X, typename BinOp>
struct ScalarBinaryOpTraits<X,CppAD::AD<X>,BinOp>
{
typedef CppAD::AD<X> ReturnType;
};
} // namespace Eigen
This requires that CppAD::AD<X>() * X() is implemented.
Alternatively, you need to cast your matrix m to AD:
// should work:
y = m.cast<CppAD::AD<double> >() * x;