2

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)

  • 1
    Facetiously, you're not supposed to inherit from a `std::vector`. Doesn't make this a bad question though (apart from the fact that you haven't provided the compiler diagnostic.) – Bathsheba Mar 22 '19 at 16:23
  • 6
    Why do you have `inline operator Vector() const`? You can't make a vector with a const data type. Having that function cause the compiler to stamp out `Vector` which fails – NathanOliver Mar 22 '19 at 16:27
  • The example is perhaps overly boiled down from the original code. The original is templatized on the container type, with std::vector being one underlying container type I hoped to support. Other containers I'm interested in support DataType = double const. – Kent G. Budge Mar 22 '19 at 16:36
  • Yep, you have a conversion operator that explicitly uses something that cannot be instantiated. This part is wrong no matter what. – Matthieu Brucher Mar 22 '19 at 16:38
  • 1
    You can't have `vector`period. So `DataType` cannot be `const double`. So your designed is fundamentally flawed. – Matthieu Brucher Mar 22 '19 at 16:38
  • I've edited my post to give an even more boiled-down version, and include the error message. – Kent G. Budge Mar 22 '19 at 16:39
  • So it looks like the problem is that `operator Vector` appearing in a declaration triggers instantiation of `Vector` whether or not the operator is actually used? But why then does the code build if the constructor taking `struct Unit` is removed? – Kent G. Budge Mar 22 '19 at 16:42
  • Possible duplicate of [Vector of const objects giving compile error](https://stackoverflow.com/questions/17313062/vector-of-const-objects-giving-compile-error) – Increasingly Idiotic Mar 22 '19 at 16:43
  • A mystery (to me) is that the copy-construction is what triggers the error. I don't see how that is related to the (ill-advised) conversion operator. (If it is related, but removing either makes the error go away, so...) – molbdnilo Mar 22 '19 at 16:57
  • molbdnilo: Yes, that's really what's puzzling me. I understand that std::vector can't be instantiated. I understand that this means Vector can't be instantiated. (The original design was for an underlying container that did support const element types; I was asked to try to extend the design to permit std::vector as the underlying container.) What puzzles me is why the compiler chooses to try instantiate Vector when the copy constructor is used, instead of either when Vector is first used (preceding line) or never. – Kent G. Budge Mar 22 '19 at 17:03
  • This compiles because you don't have the implementation of the operator, but if you link, you will see it missing. And if you implement it, then it will break. Stop doing things that you KNOW are not working. Why are you creating a conversion operator that breaks by design? This is crazy. – Matthieu Brucher Mar 22 '19 at 17:25
  • Matthieu, if you read my other comments, you will get the context. I was basically strong-armed by my team into trying to extend a well-behaved design based on a specific and well-behaved underlying container to be templatized on the underlying container type, then to try to make it work when the underlying container is std::vector. No, that clearly isn't going to work. This discussion has helped highlight why: I can't reliably keep the compiler from trying to instantiate std::vector even when I have no intention of using anything but std::vector. – Kent G. Budge Mar 22 '19 at 17:35
  • If your vector should stay small (like < 2) you should try `std::array` – Guillaume Racicot Mar 22 '19 at 17:47
  • Guillaume Racicot, alas, my vectors are going to be enormous and dynamically sized. – Kent G. Budge Mar 22 '19 at 19:11

2 Answers2

0

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.

-1

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.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • 1
    Bit of background: I originally wrote this to use a reference-counted vector class template from another library as the underlying container. `Teuchos::ArrayRCP`, from Sandia National Laboratories' Trilinos library, if you're interested. It worked just fine. Then, in code review, other members of my team suggested it ought to be templatized on the underlying container template, because we might someday want to use a different underlying container. And then insisted I ought to test it for `std::vector`. – Kent G. Budge Mar 22 '19 at 16:57
  • 1
    I didn't think this was a good idea, since std::vector is not reference counted and does not permit const element types, let alone provide a specialization for const element types. `Teuchos::ArrayRCP` does both. Kind of an "if they can slice, oil, and fry a potato chip in just five seconds, why can't we fabricate a silicon chip that fast?" pointy-haired boss kind of thing. Well, this pretty much highlights the flaws with that idea. Which probably won't actually help me much at the next team meeting. – Kent G. Budge Mar 22 '19 at 16:57