0

Improper support of volatile-qualified overloadings of member functions in STL prevents using of containers, smart pointers, etc in generic way. Say, I want to declare a wrapper class, that provides value semantics and allows incompleteness of underlying type:

#include <type_traits>
#include <utility>
#include <memory>

template< typename type >
struct recursive_wrapper
{

    using value_type = type;

    template< typename ...arguments >
    recursive_wrapper(arguments &&... _arguments)
        : storage_(std::make_unique< type >(std::forward< arguments >(_arguments)...))
    { ; }

    operator type & () & noexcept
    {
        return *storage_;
    }

    operator type const & () const & noexcept
    {
        return *storage_;
    }

    operator type && () && noexcept
    {
        return std::move(*storage_);
    }

    operator type const && () const && noexcept
    {
        return std::move(*storage_);
    }

    operator volatile type & () volatile & noexcept
    {
        return *storage_;
    }

    operator volatile type const & () volatile const & noexcept
    {
        return *storage_;
    }

    operator volatile type && () volatile && noexcept
    {
        return std::move(*storage_);
    }

    operator volatile type const && () volatile const && noexcept
    {
        return std::move(*storage_);
    }

private :

    std::unique_ptr< type > storage_;

};

// file:main.cpp
#include <iostream>
#include <vector>

#include <cstdlib>

int
main()
{
    struct A;
    struct B { recursive_wrapper< A > a; };
    struct A { std::vector< B > b; };
    { // basic usage
        B b; 
        A & a = b.a; // OK
        static_cast< void >(a);
    }
    // let's add cv-qualifiers
    {
        volatile B b; 
        volatile A & a = b.a; // error!
        static_cast< void >(a);
    }
    return EXIT_SUCCESS;
}

Lack of appropriate volatile-qualified overloading of std::unqie_ptr::operator * () causes an error:

main.cpp:38:16: error: indirection requires pointer operand ('volatile std::unique_ptr<A>' invalid)
        return *storage_;
               ^~~~~~~~~
main.cpp:83:30: note: in instantiation of member function 'recursive_wrapper<A>::operator volatile A &' requested here
            volatile A & a = b.a;
                             ^
1 error generated.

The same story WRT std::container::push_back(), size(), etc.

It totally prevents a using of objects of STL (not involving const_cast operator) in generic code, which uses volatile member-function qualifier.

What is a reason of such poor STL design decision? Why volatile member function qualifier not properly supported in STL?? Is volatile member function qualifier depricated?

Tomilov Anatoliy
  • 15,657
  • 10
  • 64
  • 169
  • I hope you're not suggesting that `std::container::push_back` should have been `volatile`, because it most certainly should not have been. – user541686 Oct 06 '15 at 07:34
  • @Mehrdad I meet your expectations. I just want to know why `std::container::push_back() volatile` overloading alongside with `std::container::push_back()` is not avaliable in STL widely? – Tomilov Anatoliy Oct 06 '15 at 07:37
  • Why would it be? You only add stuff to an API if it is useful. What's your use case for a `volatile std::vector>`? – Mat Oct 06 '15 at 07:52
  • @Orient: Because it would be wrong. You can't mutate a container that can't even rely on its own fields staying constant throughout the duration of its own methods. – user541686 Oct 06 '15 at 07:56
  • @Mat Seems the example is not clear enough. I don't know why `volatile` is not yet depricated (in light of `` presence, but maybe they are orthogonal to each other? I don't know) and why it allowed to declare `volatile std::vector<...>`, if there no `std::vector<...>::vector(...) volatile` constructor =). – Tomilov Anatoliy Oct 06 '15 at 07:56
  • 2
    `volatile` and `atomic` are unrelated (even if `volatile` were (mis)used in place of `atomic` before C++11). – Jarod42 Oct 06 '15 at 08:09
  • 2
    @Orient: Yes, they are orthogonal to each other. `atomic` is only relevant if the program is multithreaded, whereas `volatile` is relevant even if the program is single-threaded. Furthermore, `atomic` establishes memory ordering constraints between multiple threads, whereas `volatile` has undefined behavior when multiple threads are involved. (*Understanding* this requires a fairly good understanding of memory models, so if it isn't obvious, that's normal. Just take my word for it for now, and if you'd like to learn more about it, Google `atomic<> weapons C++` and watch the videos.) – user541686 Oct 06 '15 at 08:17
  • @Mat: [volatile: The Multithreaded Programmer's Best Friend](http://www.drdobbs.com/cpp/volatile-the-multithreaded-programmers-b/184403766) present (an abuse of) volatile to mark methods which should be protected by mutex and the other. (Even if first paragraph is misleading) – Jarod42 Oct 06 '15 at 08:19

2 Answers2

1

It's a very good decision. It's because volatility would be outright wrong for most types.

Keep in mind that volatile on an object means the object's fields can mutate spontaneously.
Consider the following, and assume the system guarantees that, at any given instant, begin and end will point to the same memory block:

template<class T>
class vector
{
    T *begin;
    T *end;
    vector(vector const volatile &other) : begin(other.begin), end(other.end) { ... }
};

It turns out vector::vector(vector const volatile &) is wrong, because it cannot ensure that begin and end are read simultaneously.
Consequently, the copy it creates might have a begin and end that are out of sync, even though the original was completely fine.

I think this should be enough to make you realize why volatile is barely used.
It simply isn't used for the same reason that you were probably expecting used (i.e. atomics).
Its use case is entirely different and uncommon, and it is not something you throw around on a whim the way you might with const.

user541686
  • 205,094
  • 128
  • 528
  • 886
  • @Orient: what's `dword` and what is `std::variant`? Can you link me to their definitions? I haven't seen them in C++. – user541686 Oct 06 '15 at 08:35
  • dword means here a double word (word == width of data bus). In the second I mean `std::vector` (any container, http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4542.pdf also suitable). – Tomilov Anatoliy Oct 06 '15 at 08:36
  • @Orient: `dword` is not a standard C++ type, so the answer depends on how it is defined and how you're using it. As for `std::vector`, I'm not sure, but I don't think it's UB, although it would require `T` to be volatile-move-constructible. That said, this is different from what you were asking, right? You were asking about `volatile` on member functions, and you gave `vector::size()` as an example, so I presume you meant `size_t vector::size() const volatile`... which is not the same as what you asked here. – user541686 Oct 06 '15 at 08:42
  • Yes, they were additional hardly related questions. – Tomilov Anatoliy Oct 06 '15 at 08:45
0

"Poor design decision"? Not really. The keyword is inherited from C, but there is very little use for it today. It's not actively deprecated, but its main use is in simple cases. Memory-mapped hardware would be a good example. But there won't be a memory-mapped std::deque<> so there's little point in STL support for that.

MSalters
  • 173,980
  • 10
  • 155
  • 350