This one has me baffled.
Here's the sample code in its entirety:
#include <vector>
struct Unit {};
template <typename DataType>
class Vector : public std::vector<DataType>
{
public:
typedef std::vector<DataType> base_t;
Vector() = default;
explicit Vector(Unit const &units);
bool check_class_invariant() const noexcept
{
return base_t::size() < 2;
}
operator Vector<DataType const>() const;
private:
Unit units_;
};
void foo()
{
Vector<double> a;
Vector<double> b(a); // wants to construct Vector<double const> for some reason
}
This is a much boiled down version of code that actually does something interesting.
Compilation fails with g++ 8.1.0. The error messages indicate that the compiler is trying to instantiate Vector<double const>
at the indicated line, which implies instantiating std::vector<double const>
, which is forbidden by the standard.
Here's the full error message:
In file included from /scratch/vendors/spack.20180425/opt/spack/linux-rhel7-x86_64/gcc-4.8.5/gcc-8.1.0-3c5hjkqndywdp3w2l5vts62xlllrsbtq/include/c++/8.1.0/vector:64,
from /home/kgbudge/src/core/src/utils/test/test.cc:1:
/scratch/vendors/spack.20180425/opt/spack/linux-rhel7-x86_64/gcc-4.8.5/gcc-8.1.0-3c5hjkqndywdp3w2l5vts62xlllrsbtq/include/c++/8.1.0/bits/stl_vector.h: In instantiation of âclass std::vector<const double, std::allocator<const double> >â:
/home/kgbudge/src/core/src/utils/test/test.cc:6:7: required from âclass Vector<const double>â
/home/kgbudge/src/core/src/utils/test/test.cc:30:23: required from here
/scratch/vendors/spack.20180425/opt/spack/linux-rhel7-x86_64/gcc-4.8.5/gcc-8.1.0-3c5hjkqndywdp3w2l5vts62xlllrsbtq/include/c++/8.1.0/bits/stl_vector.h:351:21: error: static assertion failed: std::vector must have a non-const, non-volatile value_type
static_assert(is_same<typename remove_cv<_Tp>::type, _Tp>::value,
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/scratch/vendors/spack.20180425/opt/spack/linux-rhel7-x86_64/gcc-4.8.5/gcc-8.1.0-3c5hjkqndywdp3w2l5vts62xlllrsbtq/include/c++/8.1.0/bits/stl_vector.h:354:21: error: static assertion failed: std::vector must have the same value_type as its allocator
static_assert(is_same<typename _Alloc::value_type, _Tp>::value,
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
make[2]: *** [src/utils/test/CMakeFiles/Ut_utils_test_exe.dir/test.cc.o] Error 1
make[1]: *** [src/utils/test/CMakeFiles/Ut_utils_test_exe.dir/all] Error 2
Why is the compiler trying to instantiate Vector<double const>
?
Clues: If I change the units_
member from struct Unit
to int
, the code compiles.
If I remove either the constructor taking a single struct Unit
argument, or the conversion operator to Vector<DataType const>
, the code compiles.
Any ideas what is happening here?
(edit: My question is not why std::vector cannot be instantiated for double const. It is why the compiler is trying to instantiate std::vector in the first place.)
(Further edit: See the comments for an explanation of the context for this question.
It looks like this may solve my problem:
struct Unit
{
};
template <typename DataType> class Vector;
template <typename DataType> class Vector<DataType const>;
template <typename DataType> class Vector : public std::vector<DataType>
{
public:
typedef std::vector<DataType> base_t;
Vector() = default;
// explicit Vector(Unit const &units);
bool check_class_invariant() const noexcept { return base_t::size() < 2; }
operator Vector<DataType const>() const;
private:
Unit units_;
};
void foo()
{
Vector<double> a;
Vector<double> b(a); // wants to construct Vector<double const>
}
The code now builds, because presumably no attempt is made to instantiate operator Vector<double const>
if it is unused and if Vector<double const>
is declared as a specialization but not (yet) defined.
I'm not sure how reliable this is, though.)
(Further further edit: No, not reliable. The translation system is determined to try to instantiate Vector<double const>
if Vector<double const>
appears as a completed type anywhere in the Vector<double>
interface, whether I intend to use that part of the interface or not.
And to close this discussion: The context is that Vector
was originally written to use a different underlying container than std::vector
, and it worked fine since that container supported const
element types. I was tasked to try to extend this to work with std::vector
as the underlying container. Having an operator Vector<Database double>
was a necessary requirement for the original underlying container type, which I must still support, and I can't discard it. So the answer is that I can't extend this to std::vector
as the underlying container.
Unless my team decides I should write a specialization just for std::vector
. Sigh)