c++c++11operator-overloadingstdvectorsubscript-operator

Overloading the class subscript operator to access elements of a member std::vector object


I am parsing a text based file to read variables from it. Existence of variables in the file is important, so I decided to write a template class which will hold both value of the variable (Value) and its existence flag (Exists).

template<class Type>
class MyVariable
{
    public:
        Type    Value;
        bool    Exists;
        MyVariable()
            : Exists(false), Value(Type())
        {
        }
        MyVariable(const Type & Value)
            : Exists(true), Value(Value)
        {
        }
        MyVariable(const Type && Value)
            : Exists(true), Value(std::move(Value))
        {
        }
        MyVariable(const Type & Value, bool Existance)
            : Exists(Existance), Value(Value)
        {
        }
        MyVariable(const Type && Value, bool Existance)
            : Exists(Existance), Value(std::move(Value))
        {
        }
        size_t size() const
        {
            return Value.size();
        }
        const MyVariable & operator=(const MyVariable &  Another)
        {
            Value   = Another.Value;
            Exists  = true;
        }
        const MyVariable & operator=(const MyVariable && Another)
        {
            Value   = std::move(Another.Value);
            Exists  = true;
        }
        const Type & operator[](size_t Index) const
        {
            return Value[Index];
        }
              Type & operator[](size_t Index)
        {
            return Value[Index];
        }
        operator const Type & () const
        {
            Value;
        }
        operator Type &()
        {
            Value;
        }
};

The stored variable type will occasionally be std::vector, so I overloaded the subscript operator operator[] to directly access the elements of the vector. So that I can make the Value and Exists members private.

I use this class like this in the code:

const MyVariable<std::vector<int>> AVector({11, 22, 33, 44 ,55});
for (size_t i=0; i<AVector.size(); i++)
{
    std::wcout << L"Vector element #" << i << L" --> " << AVector.Value[i]  << std::endl;   // Works okay.
    std::wcout << L"Vector element #" << i << L" --> " << AVector[i]        << std::endl;   // Gives error.
}

I get the following error message:

Error C2679 binary '<<': no operator found which takes a right-hand operand of type 'const std::vector<int,std::allocator<_Ty>>' (or there is no acceptable conversion)

What am I doing wrong here?


Solution

  • TartanLlama's and songyuanyao's answers are correct only when the contained variable type (i.e.; ValueType) is std::vector. If we attempt to store a fundamental data type (e.g.; int or float), the compiler (MSVC14) gives the error below since there won't be any implicit subscript operator operator[] or value_type member type definition inside.

    'InputFileVariable<bool,std::string>::value_type': is not a type name, static, or enumerator  
    'InputFileVariable<int,std::string>::value_type': is not a type name, static, or enumerator  
    'InputFileVariable<uintmax_t,std::string>::value_type': is not a type name, static, or enumerator  
    'InputFileVariable<float,std::string>::value_type': is not a type name, static, or enumerator
    

    I found the solution by using function templates. I rewrote the subscript operators as templates, so that the compiler doesn't create the subscript member functions unless they are called. And since I call them only when the stored element is an std::vector, it doesn't cause any problem with fundamental types.

    My working final code is below.

    #include <vector>
    #include <string>
    
    template<class ValueType, class KeyType = std::string>
    class InputFileVariable
    {
        public:
            const KeyType   Key;
            ValueType       Value;
            bool            Exists;
            InputFileVariable(KeyType && Key, ValueType && Value, bool Existance = false)
                :   Key     (std::forward<KeyType>  (Key)),
                    Value   (std::forward<ValueType>(Value)),
                    Exists  (Existance)
            {
            }
            size_t size() const
            {
                return Value.size();
            }
            const InputFileVariable & operator=(InputFileVariable && Another)
            {
                Key     = std::forward<InputFileVariable>(Another).Key;
                Value   = std::forward<InputFileVariable>(Another).Value;
                Exists  = true;
                return *this;
            }
            template <class ElementType = ValueType::value_type>
            const typename ElementType & operator[](size_t Index) const
            {
                return Value[Index];
            }
            template <class ElementType = ValueType::value_type>
            typename ElementType & operator[](size_t Index)
            {
                return Value[Index];
            }
            operator const ValueType & () const
            {
                return Value;
            }
            operator ValueType & ()
            {
                return Value;
            }
    };
    
    int wmain(int argc, wchar_t *argv[], wchar_t *envp[])
    {
        // Used with "std::vector":
        InputFileVariable<std::vector<int>> MyVar1("MV1", {2, 4, 6, 8}, true);
        const size_t SIZE = MyVar1.size();
        std::cout << "Size = " << SIZE << std::endl;
        int Temp = MyVar1[1];
        MyVar1[1] = MyVar1[2];  // Here we call both the const and non-const operators.
        MyVar1[2] = Temp;
        for (size_t i=0; i<SIZE; i++)
        {
            std::cout << "MyVar1[" << i << "] = " << MyVar1[i] << std::endl;
        }
    
        // Used with "double":
        InputFileVariable<double> MyVar2("MV2", 3.14, true);
        std::cout << std::endl << "MyVar2    = " << MyVar2 << std::endl;
    
        std::cout << std::endl;
        _wsystem(L"timeout /t 60 /nobreak");
        return 0;
    }
    

    Output:

    Size      = 4
    MyVar1[0] = 2
    MyVar1[1] = 6
    MyVar1[2] = 4
    MyVar1[3] = 8
    
    MyVar2    = 3.14