11

BEFORE YOU READ: const_reference is typedef and does not need to be const T& as you can see in std::vector<bool>::const_reference = bool. Please keep that in mind while reading the rest to understand it properly (as suggested in commets, this is hard for many people).


I wanted to use STL containers for simple types (e.g. int) and found that they use the sub-optimal const T& "anti-pattern" - it works well for big classes, but is sub-optimal for simple/fundamental types when not inlined - consider embedded system, e.g. on ARM/ATSAM4L, with instantiation.

The question is: why was e.g. vector::push_back redesigned with argument of (const value_type&) instead of (Allocator::const_reference) since C++11? It should be the same for generic allocator, but doing it the other way would help me to write a custom allocator (or template specialization) for fundamental types that would define const_reference being the type itself (see vector<bool>::const_reference = bool).

Question 2: Is there any adaptor class that can do that for me?

Something like this:

template<class T, class base = std::vector<T> >
  class myvect: protected base {
public:
    typedef T const_reference;
    void push_back(const_reference value) {
        base::push_back(value); }}

Final usage would by like this:

typedef void (*action_t)(void*,int);
extern "C" void work(void *, action_t);
work(&vect, (action_t)&vect::push_back);

(note: ignore possible casting problems in previous code block, I hope you got the idea.)

EDIT: vector::const_reference is defined directly to be const value_type& but should in my opinion be defined as Alloc::const_reference which could then easily be changed (vector<int, MyAllocator<int> >). (This changed in C++11, it was defined as Alloc::const_reference but is now const value_type&)

EDIT: func(const T&) is sometimes described as "anti-pattern" here on stackoverflow because it is sub-optimal for fundamental types without inlining (yes, compiler generates optimal code even for func(const int&) if it gets inlined, but here is that IF. func(int) will do better work)


CONCLUSION: The very problem seems to be in the behaviour of const and therefore const T&. const does not really mean shall not change but do not change and therefore const T& is needed (and well defined and used) as return type for many methods. Creating custom adaptor class seems to be the best way for optimization and it seems to be widely accepted, that std::vector<bool> is odd exception which should be in separate class (e.g. dynamic_bitset).

firda
  • 3,268
  • 17
  • 30
  • 2
    What is the difference between the two types? IMHO, std::vector::const_reference is equivalent to std::vector::value_type const&. vector is very special and defining const_reference as the type itself would invalidate the vector interface because e.g. front() must return a reference. Your optimization would make it impossible to do e.g. &v.front() to get a pointer to the memory and use vector as a replacement for C-style dynamic arrays. – Jens Jul 30 '14 at 07:25
  • 5
    aren't `const value_type&` and `const_reference` the exact same type? – stijn Jul 30 '14 at 07:25
  • 2
    I expect that the code is inlined anyway and that the resulted asm would be the same between `std::vector::push_back(const int&)` and your expected signature `std::vector::push_back(int)`. – Jarod42 Jul 30 '14 at 07:38
  • @Jens reference and const_reference are different (reference = T&, const_reference = T in my case, not breaking your &v.front() for non-const and you should use vector::data() – firda Jul 30 '14 at 07:44
  • 1
    You need to reshape your question a little bit, here is a little help about how : people don't understand that you want to do a "policy", please explain much further up in your question that you want the "reference_type" to be customizable. In people's mind reference_type is fixed by internal implementation therefore they assume you are speaking about two of the same things exactly. Your question is just slightly ill-shaped, you can fix it. – v.oddou Jul 30 '14 at 07:44
  • @stijn: yes, in current definition they are and this is what am I talking about: the question is WHY? and second: is there some *adaptor* that can change that? – firda Jul 30 '14 at 07:45
  • 1
    @v.oddou: well, I can understnd what you say (and why I already got -2 on that question), but think the other way: I have already explicitly stated `const_reference = T` would be optimization, how can I do that better? big exclamation marks with "hey people, think twice, const_reference is typedef and think again, does not always need to be equivalent to const T& as you can see in vector!" ?? – firda Jul 30 '14 at 07:48
  • vs2013: http://msdn.microsoft.com/en-us/library/xba6b5sf.aspx, `typedef const value_type& const_reference;`, and in vector: http://msdn.microsoft.com/pl-pl/library/943xt47h.aspx: `typedef typename Allocator::const_reference const_reference;`. So to me it is `const value_type&` – marcinj Jul 30 '14 at 07:51
  • 1
    @marcin_j: but vector takes two template arguments, second being the allocator which CAN BE CHANGED! thus vector with vector::const_reference = MyAllocator::const_reference = int would do the optimization without changing std::vector (if the specification would say that vector::const_reference = Alloc::const_reference instead of it being const T& for generic vector and bool for specialized vector) – firda Jul 30 '14 at 07:53
  • @firda data() is required to be data() == &front(). – Jens Jul 30 '14 at 07:57
  • @firda but vector (at least for vs2013) uses `Allocator::const_reference` for this typedef, isn't that whay you want? Anyway this name `const_reference` strongly suggest that this type should be `const T&` so not sure if you can make it `const T`. I am not sure also what you mean by `Anti Pattern`, can you give some more info on that? – marcinj Jul 30 '14 at 08:00
  • 1
    @firda: you are trying to answer half of the question by yourself. this is actually in the stack exchange guidelines as a not-to-do thing. the const_reference typedef is just you feeling like there is an opportunity there for your goals. actually I say that you are misguided to believe that, as many other replied. you don't need to care about the const_reference, you need to care that the parameter is "const value_type&" when it could be "pass_for_copy_param_type", that could be user-configured. but const_reference cannot be changed because it is used on return types and other important places – v.oddou Jul 30 '14 at 08:08
  • @marcin_j: for vs2013: that is nice, but I am afraid, that I cannot count on that (GCC may have that implemented as const T&). Can somebody please point me to official STL specification? thx. About anti-pattern: const T& is reference and thus actually a pointer (const int& is internally like int*). I am firmware programmer - tiny microchips with tiny memory, optimization is critical in such small devices as I have run out of space many times ;) – firda Jul 30 '14 at 08:16
  • 1
    @v.oddou: no, I am not answering my question, I am just defending the opinion against specific 'why not' would-be answers. I can understand your answer like that: although it could be good idea to have some other typedef for pass_for_copy_param_type used on many places instead of const_reference, there are cases where const_reference is needed as it is. My answer is: then tell me why vector::const_reference = bool? – firda Jul 30 '14 at 08:23
  • 1
    @firda: very good remark. this is because `vector` is "not a container", this is slightly philosophical, but many have agreed that vector is an oddity that should get deleted from the standard, in favor of bitfield. but this is off topic. The vector cannot use bool references because it does NOT store booleans. The fact that the parameter passing becomes by value, might be an optimization but this is a side effect. – v.oddou Jul 30 '14 at 08:29
  • here a link: http://en.wikipedia.org/wiki/Sequence_container_%28C%2B%2B%29#Specialization_for_bool . and another one : http://www.gotw.ca/publications/N1211.pdf notably clearly mentions that vector::reference is a class (some kind of proxy) and not bool itself. so its very inconsistent with const_reference. (it provides the mutation capability). – v.oddou Jul 30 '14 at 08:32

3 Answers3

6

Its a bit long in a comment, so I'll use an "answer" to speak about it, but really this is not an answer.

The feature you want, reworded:

You want te be able to avoid "pass by const reference" idiom in certain methods, to avoid the "slowdowns" incured by pointer operation for small types, versus direct copy. Notably true for push_back, if push_back had a parameter with a configurable type (by template means in the container type, like the allocator template parameter), then it could make you feel good.

It would be possible to provide the feature you want, definitely, for example by using the same method than the allocator template parameter, there could be a typename that specifies what kind of type is used in "push back" parameter.

But it was not designed that way, because it seems like a complexity, that is not worth it at all. It is not worth it because of compiler optimizations that will ellude a lot of overhead for fundamental types, because static analysis is much easier to run on fundamental types.

Secondly, the "lost time" of passing an int by reference versus copying it in a method parameters is deemed to be a platform specificity, that cannot be verified at the "virtual machine" level (the level at which language writers have to place themselves). Not to mention the "premature optimization" here that would cause a frank amout of code bloat to obtain, as a reasonable tradeoff for general purpose, it was completely evicted. This is also because at design time, you think fundamental types like rarities versus the infinity of possible types that can be contained.

ps: lastly, using a typedef as part of the allocator seems like a very bad separation of responsibility, having its own template parameter seems cleaner, because it has nothing to do with allocation, rather it has something to do with implementation choices of specific methods of the container.

ps2/Edit: I wish to add, as said in the comments, that the vector<bool>::const_reference cannot be taken as an example of what is possible to do, because it was a mistake in the standard, and is actually an infringement of the STL requirement that a container defines ..::reference as T&. A further proof of the problem is that vector<bool>::reference (not const) is not bool, it is an unspecified class (_Bit_reference in gcc) that serves as a proxy for access and mutation of a bit, packed in the vector, that shall serve as the storage support for the "virtual" bool value that is made into appearing like existing. These oddities cannot be ported to generic vector because they don't respect wide STL requirements. But it does not invalidate an orthogonal approach, like I mentioned with the pass_for_copy_param_type that could be a parameterizable, new typedef, and used in replacement in some places (suitable) where "const value_type&" is written today. But this is exactly the code bloat I mentioned before. I'm sure is definitely not worth so much for so little.

v.oddou
  • 6,476
  • 3
  • 32
  • 63
  • good answer, I will consider accepting it. I have to disagree with you in the part of 'serious engineer' - have you ever wrote a program for microprocessor with 64kB memory? Or even 8kB? Than please think again. – firda Jul 30 '14 at 08:45
  • 1
    I have accepted this answer (instead of the one from juanchopanza), because it adresses the whole question (whith all its part) instead of simply answering the title. Thank you – firda Jul 30 '14 at 10:05
4

Changing the meaning of const_reference would break the semantics of methods that return a const reference:

std::vector<int> my_vector(100);
 // this needs an lvalue ref
my_vector[42]++;

// I want to track this value
std::vector<int>::const_reference i = my_vector[3];
  // I expect i to be be 1 after this
my_vector[3]++;
juanchopanza
  • 223,364
  • 34
  • 402
  • 480
  • const_reference = T, reference = T&. my_vector[42]++ is fine, your second example uses aliasing (it should be described as volatile_const_reference I think) which is not a good practice. – firda Jul 30 '14 at 08:35
  • 1
    @firda Whether it is good practice or not is a matter of opinion. Still, it is something you can expect to be able to do with a standard library container's `const_reference`. – juanchopanza Jul 30 '14 at 08:38
  • Well, the question was "why it was designed like that + do we have some adapter doing what I want?" and your answer thus is "because it was accepted that aliasing is allowed, that front() and indexing can be and often is used to track the stored value (not a copy or immutable object... and I can think about mutable attribute here)" (second question unanswered). I think all this is actually a problem of what 'const' means and how it can be used (const does not really mean 'never changed', but 'do not write'). ...allright, two answeres here worth accepting I think.I'll get back to it later today – firda Jul 30 '14 at 09:03
2

Allowing changing the type of const_reference breaks compatibility to the std::vector and container requirements. The standard explicitly assumes that const_reference is a reference and uses this many times, e.g. the semantics of data() and front(). In 23.3.6.4, it says "For a non-empty vector, data() == &front()". This make your modification invalid because with your modification, front() would not return a reference to the allocated memory anymore and hence the addresses would differ. In fact, it would be an address of a temporary object.

I also think that most compilers will optimize any additional costs of using const& away.

std::vector< bool > is a bad example because it is not a model of container in the STL sense. Most people agree that it should not have been defined in the standard, e.g. https://isocpp.org/blog/2012/11/on-vectorbool.

Jens
  • 9,058
  • 2
  • 26
  • 43
  • again: &front() (or &vect[0]) is now common practice, I can understand that. my change won't change that behaviour for standard vector with standard (and common) allocator. all I am suggesting is to reshape vector template that it's behavour can be alterent through the allocator (and yes, &myvect.front() would be ill, but my vect is not std::vect but std::vect with MyAllocator::const_reference = int) – firda Jul 30 '14 at 08:00
  • well, every std::vector > has to be compliant to the semantics in the standard, and this explicitly states that data() == &front(). This could be changed of course, but honestly I don't see the benefit of doing that. Could you elaborate on the benefit and why this is actually an anti-pattern? I have never heard of this, but I am not an embedded programmer. I have the feeling that compiler will optimize any overhead for fundamental types away. – Jens Jul 30 '14 at 08:04
  • std::vector is not compliant to the semantics, why you insist that std::vector has to be? – firda Jul 30 '14 at 08:26
  • Because that is what is mandated by the standard and what I (and most programmers) expect. I want to able to write generic code that works with all vector, independent on their implementation details what I consider allocators to be. They provide a policy to customize internal specifics of the vector, but not how it works on the outside. Most people discourage using vector exactly for this reason because it looks like vector but breaks semantics. Even members of the standard committee think so: https://isocpp.org/blog/2012/11/on-vectorbool – Jens Jul 30 '14 at 08:35
  • @firda: Because the standard explicitly says vector is an exception. – MSalters Jul 30 '14 at 08:35
  • ahh, yes: in other words: vector is actually *bad* exception that sould not be in standard as well as my vector. is that your answer? `bitset` or other name (like simple_vector) should be used for `adaptor` with different behaviour. I can accept that. – firda Jul 30 '14 at 08:41
  • @firda Yes, that is what I am saying. I would not break the contract with clients based on a template policy. – Jens Jul 30 '14 at 09:02