0

I am looking to use the expressions passed in the operator []. I thought that using variatic template arguments would do the trick but i was wrong... Is the a way to do this in c++11?

class object {
 

private:


public:
  void values() { std::cout << "finished" << std::endl; }
  template <typename T, typename... Type> void values(T arg, Type... args) {

    std::cout << arg << "  " << std::endl;
    values(args...);
  }


   template<typename... Type> void operator[](Type... args) {

       values(args...);
  }
};

int main(void) {
  object o1 = object();

  o1.values(1, 6.2, true, "hello"); // Works fine.
  
  o1[1, 6.2, true]; // Only the last value gets printed eg. true
  

  return 0;
}

The broader objective is that I was asked to make a working syntax of this

let o3 = object [ values 1, "2", true, -3.14 ];
let o1 = object [  key("x") = -1, key("y") = -2,values 1, "2", true, -3.14 ]; // no commas are missing

in c++11 using c++11 STL (templates , using , MACROS , operator overloading etc.) . I am slowly trying to figure out how to piece this together

Lampros
  • 321
  • 3
  • 10
  • 3
    `operator[]` only takes one argument (at least pre C++23). – Jarod42 Dec 16 '21 at 13:55
  • Bummer...Is there another way of achieving this? – Lampros Dec 16 '21 at 13:56
  • if you call it with `o1.operator[](...);` it gives you a compile time error: https://godbolt.org/z/Ghvhvhese Your version only compiles because the comma operator discards the first and second value. – mch Dec 16 '21 at 14:01
  • You can use the `operator()` instead, which can have any number of arguments. – mch Dec 16 '21 at 14:01
  • what is the aim of this? You can always use a named method rather than an operator. There are some tricks one can play, eg `o1[ some_magic(1),6.2, true]` would work, but I don't think it is possible to enable `o1[1,6.2,true]` – 463035818_is_not_an_ai Dec 16 '21 at 14:02
  • operator overloading has the benefit of lots of code expecting certain behavior, but no code expects `operator[]` to take more than 1 parameter, hence why bother? – 463035818_is_not_an_ai Dec 16 '21 at 14:04
  • I am asked to make a working syntax of this `let o3 = object [ values 1, "2", true, -3.14 ]; ` and this `let o1 = object [ key("x") = -1, key("y") = -2,values 1, "2", true, -3.14 ];` in c++11. I am slowly trying to figure out how to piece this together. – Lampros Dec 16 '21 at 14:08
  • I'd try overloading `,` operator, that's the most you can count on imho. – alagner Dec 16 '21 at 14:08
  • please include this requirement in the quesiton. what is `let` and `values` in `let o3 = object [ values 1, "2", true, -3.14 ];` and is there a `,` missing ? – 463035818_is_not_an_ai Dec 16 '21 at 14:10
  • Sorry i will add it, but it is way beyond the scope of my question, that is why i removed it before asking. – Lampros Dec 16 '21 at 14:16
  • see [xy problem](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem). It isnt beyond the scope of the problem, because what you asked for is not possible (try a non template `operator[](int a,int b)` to see a clear error, you simply cannot have `operator[]` take more than 1 argument), but there might be other ways to solve your actual problem – 463035818_is_not_an_ai Dec 16 '21 at 14:18
  • again, what is `let` and `values` ? And is there a `,` missing in `values 1` ? – 463035818_is_not_an_ai Dec 16 '21 at 14:22
  • That is part of the problem.I imagine that object is a subclass of let. And no commas are missing. – Lampros Dec 16 '21 at 14:27
  • are you sure that you are supposed to write C++? It looks like javascript, though I don't know javascript – 463035818_is_not_an_ai Dec 16 '21 at 14:34
  • Thats exactly the point. Make this syntax functional in C++. – Lampros Dec 16 '21 at 14:37
  • @Lampros the closest I managed to get during the last 20 minutes was [this](https://godbolt.org/z/j53c9vEcb). I wouldn't say it's a good code, but if you need this kind of monstrosity for some reason, knock yourself out ;) – alagner Dec 16 '21 at 14:52

1 Answers1

5

First of all, you need to understand that operator[] can only take exactly one argument. Your templated operator hides an error message that is rather clear about that.

struct foo {
    void operator[](int,int);
};

Results in error:

<source>:2:10: error: 'void foo::operator[](int, int)' must have exactly one argument
    2 |     void operator[](int,int);
      |          ^~~~~~~~

You can make it a variadic template, but any instantiation with not exactly one argument isn't right.


Franky, this sounds like a trick question. When you write C++ code you better use C++ syntax. If macros are allowed (they shouldn't be) then almost anything is possible. You just need to be aware that you aren't actually writing C++ anymore. Also, it seems a bit odd to merely ask for some syntax to compile. I don't know javascript and wasn't sure what the two lines are supposed to mean, so I only did that: Make it compile somehow.

Anyhow, lets see what can be done.

let o3 = object [ values 1, "2", true, -3.14 ];

I suppose this declares o3 to be an object whose initializer is retrieved from a container called object that can be indexed via values 1, "2", true, -3.14. The line can be made to compile by overloading some operator, and #defineing let to be auto and values to construct an object that collects the index (via its operator,):

For the second line

let o1 = object [  key("x") = -1, key("y") = -2,values 1, "2", true, -3.14 ]; 

a similar trick can be played with operator, and abusing key::operator=. I interpreted key as constructing some key-value pair,eg key("x") = -1 maps the value -1 to the string "x". What it actually does isn't essential for all the dirty stuff that follows. Once you understood how to misuse operator overloading it can be modified to do something else in the details.

To see that all values are actually passed to operator[] I stole some tuple print function from here: https://stackoverflow.com/a/41171552/4117728

#include <tuple>
#include <string>
#include <typeinfo>
#include <iostream>
#define let auto
//https://stackoverflow.com/a/41171552/4117728
template<class TupType, size_t... I>
void print(const TupType& _tup, std::index_sequence<I...>)
{
    std::cout << "(";
    (..., (std::cout << (I == 0? "" : ", ") << std::get<I>(_tup)));
    std::cout << ")\n";
}

template<class... T>
void print (const std::tuple<T...>& _tup)
{
    print(_tup, std::make_index_sequence<sizeof...(T)>());
}
//........................................

struct Object {
    template <typename ...T>
    struct ObjectIndex {
        ObjectIndex() {}
        ObjectIndex(std::tuple<T...> ind) : index(ind) {}
        std::tuple<T...> index;        
        template <typename U> 
        ObjectIndex<T...,U> operator,(const U& u){ 
            return { std::tuple_cat(index,std::make_tuple(u)) };
        }
        template <typename...U>
        ObjectIndex<T...,U...> operator,(const ObjectIndex<U...>& other) { 
            return { std::tuple_cat(index,other.index) };
        }
    };
    template <typename ...T>
    int operator[](ObjectIndex<T...> index){
        std::cout << typeid(index.index).name() << "\n";
        print(index.index);
        return 42;
    }
};

struct key {
    std::string k;
    int val;
    key(const std::string& s) : k(s) {}
    Object::ObjectIndex<std::string,int> operator=(int v) {
        val = v;
        return {std::make_tuple(k,val)};
    }

};

#define values Object::ObjectIndex<>{} ,

int main() {
    Object object;
    let o3 = object [ values 1, std::string("2"), true, -3.14 ];
    let o1 = object [  key("x") = -1, key("y") = -2,values 1, std::string("2"), true, -3.14 ]; 
}

Live Demo

Don't do this at home (or anywhere else)!

There was some issue with passing a string literal and I didn't bother to look into that further, so I allowed myself to replace "2" with std::string("2"). I guess with some more fighting through endless error messages this can be fixed too.

I have no clue if the code is anywhere near to what those two lines are really supposed to do. I merely took it as a fun exercise to get it compile somehow. When actually returning something from operator[] I ran out of ideas. I have a hard time to imagine a container with a truly variadic operator[]. I choose the answer that is right always.


TL;DR Can you overload operator[] to take more than one parameter? No. Can you overload it to take a single parameter that encapsulates arbitrary values? Sure.

463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185
  • Thank you! Not exactly what i was looking for but it is entirely my fault ,and it not compiling in c++11 .It has many things in it that will help me alot , mainly the "Can you overload it to take a single parameter that encapsulates arbitrary values? Sure." part! Does this have anything to do with the `<<` operator overload complaining about it being a binary operator , but through some ways making it accept 2 arguments? (Found some ways using `friend` to achieve this). Again thank you for your time ! – Lampros Dec 16 '21 at 15:58
  • @Lampros I dont understand what you mean with "the `<<` operator overload". What I mean with passing more than one value to `operator[]` is that you can eg pass a `std::vector` or a `std::pair` or a `std::tuple` or whatever – 463035818_is_not_an_ai Dec 16 '21 at 17:46