简体   繁体   中英

Compiler trying to instantiate std::vector<double const> for no obvious reason

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 )

So the solution was to declare a specialization of my class for const elements, but never define it, since I never mean to use it. This builds, links, and runs correctly.

It avoids getting rid of the conversion operator, which I need for the original underlying container type.

To instantiate your type, it must be possible to instantiate operator Vector<DataType const>() const .

And, to do that, it must be possible to instantiate Vector<DataType const> , which it isn't, because you can't have a const std::vector value type.

Simply remove that operator: it won't work.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM