In this question it is said that it is better to transfer primitive types to a function by value. As I understand it, the same is true for returning from a function. Then why are indexing operators (for example, vectors ) returning links only, and not specialized for return by value for fundamental types? Especially since semantics will probably be used.

upd what i want to do:

 #include <iostream> template<typename T> struct A { T x; T& operator[](int) {return x;} const T& operator[](int) const {return x;} }; template<> struct A<int> { int x; int& operator[](int) {return x;} int operator[](int) const {return x;} }; int main() { const A<int> a{}; A<int> b{}; //a[0] = 3; b[0] = 3; std::cout << a[0] << ' ' << b[0]; } 
  • if I understand the question correctly, then because the user wants to write like this v[1] = 1 . If returned by value, it will be bad. - KoVadim
  • @KoVadim for a constant container, naturally - anton
  • in c ++ you cannot overload functions by the return value. Therefore, it is difficult to make it so that both the primitive type returns and the link. - KoVadim
  • @KoVadim I didn’t write to overload - use type_traits - anton
  • @KoVadim to make specialization for value_type = int, double, etc. - anton

3 answers 3

If you specialize for all the fundamental types of something like a standard container that possesses a variety of different methods, it will be very inefficient.

Therefore, the indexing operator in any case returns a reference to the object. Passing by reference is not a resource-intensive operation. Objects are not created or copied. Moreover, for fundamental types, the displacement constructor, as such, does not exist. Therefore, there is no need to specifically overload standard containers for fundamental types, which are quite a few.

In addition, we get an ambiguity with the definition of an overloaded indexing operator, since the built-in operator always returns lvalue .

There is another nuance. Even if you are not going to assign a new value to the returned object, you may still need to store pointers to these objects. For example, you may need to create an object of type std::reference_wrapper by using the function std::cref , which will point to the original object in the container. If you do not send a link to the object from the container, then you will not be able to take advantage of this opportunity.

Consider the following example.

 #include <iostream> #include <functional> #include <algorithm> int main() { const std::vector<int> v = { 3, 2, 4, 5, 1 }; std::vector<std::reference_wrapper<const int>> rv = { std::cref(v[0]), std::cref(v[1]), std::cref(v[2]), std::cref(v[3]), std::cref(v[4]) }; std::sort(rv.begin(), rv.end(), [](auto &a, auto &b) { return a.get() < b.get(); }); for (int x : v) std::cout << x << ' '; std::cout << std::endl; for (const auto &r : rv) std::cout << r.get() << ' '; std::cout << std::endl; } 

Output of the program to the console

 3 2 4 5 1 1 2 3 4 5 

In this program, declared a constant vector. However, since operator [] returns a reference to the source object, you can link it with the object std::reference_wrapper , and thus represent the elements of the source vector as ordered by different criteria.

That is, it does not matter whether the object is constant or not, however, it is often necessary to get a link to it in order to track its state. Constant objects can, among other things, also be volatile objects or have data members with the mutable modifier.

If you return an object by value from an operator, then the connection with the original object will be lost, and you will be dealing with a copy.

  • But is it possible with the help of TMP to conduct only one "specialization"? In theory, the code for all primitive types will be the same? (This is the first argument about efficiency, the second argument that indexing returns a link is understandable.) - VladD
  • @VladD This will only litter the language and standard of the language for the sake of incomprehensible and dubious advantages. - Vlad from Moscow
  • @VladD is_same <int> and is_same <double> etc - anton
  • @VladfromMoscow clog up the sources?) There is so much darkness, I think there is optimization and worse, if it really is optimization - anton
  • Can you describe the third item in more detail? when do we need constant pointers to primitive types in return for their own? - anton

I think this is what is required.

 template <class T> struct A { using ConstIndexRetType = typename std::conditional<std::is_fundamental<T>::value, T, const T&>::type; T x; T& operator[](int) { return x; } ConstIndexRetType operator[](int) const { return x; } }; int main(int argc, char **argv) { struct C {}; const A<int> a; const A<C> b; static_assert(std::is_same<decltype(a[1]), int>::value, "?"); static_assert(std::is_same<decltype(b[1]), const C&>::value, "?"); } 
  • Yes, something similar, but, again, I ask, why is this not implemented in the standard library? Does it really benefit? - anton
  • one
    @anton, I don’t take responsibility for the STL developers, but I assume: in addition to the standard types, there can be all sorts of struct Q {char a [2]; }, which also occupy a couple of bytes. In theory, it makes sense to pass them by value. Then need to filter by type size? No, because there may be a non-trivial copy constructor (copying data located at the pointer, for example) - int3

And let's look at the practice ... So to say, find the three differences: https://godbolt.org/g/s95gJt

This

 void f(const A<int>& a, A<int>& b) { std::cout << a[0]; std::cout << b[0]; } 

compiled into

  push rbx mov rbx, rsi mov esi, DWORD PTR [rdi] mov edi, OFFSET FLAT:std::cout call std::basic_ostream<char, std::char_traits<char> >::operator<<(int) mov esi, DWORD PTR [rbx] mov edi, OFFSET FLAT:std::cout pop rbx jmp std::basic_ostream<char, std::char_traits<char> >::operator<<(int) 

As you can see, there is no difference.

  • This is code without optimization. If you add it a little, it will be generally good :) I wrote about it above - the compiler will simply substitute the constants (in your case, just zeros). - KoVadim
  • @KoVadim I removed this piece so as not to embarrass immature minds :) - you can see the edits. Or the link - there it is also shown. - Harry