4

I have been looking at creating a synchroniser helper template class which is based on Herb Sutter's ideas of a wrapper class in this talk This does not work in msvc as is (unless we remove the brace initialisation) but when brace initialisation is removed then it's fine.

In clang/gcc (ubuntu 12.10, gcc4.7.2, clang (3.2) self built with libc++) it seems the private access modifier has to appear before the public: which seems a little strange.

The error in gcc is error: ‘t_’ was not declared in this scope

and clang is

error: use of undeclared identifier 't_'
  auto operator()(F f) const ->decltype(f(t_))

It may be a template/declytpe issue that I am not aware of and wonder if anyone can help with this one. (all compiled with relevant c++11 flags)

template <class T>
class Synchronised {
    public:
        Synchronised(T t = T{}) : t_{t} {}
        template <typename F>
        auto operator()(F f) const -> decltype(f(t_)) {
            std::lock_guard<std::mutex> lock{mutex_};
            return f(t_);
        }
        private: // place this before public: and this object compiles
            mutable T t_;
            mutable std::mutex mutex_;
};

Edit: Adding Johannes's ideas and full class in case anyone wants a cut and paste.

#include <future>
#include <iostream>
#include <thread>
#include <vector>

template <class T> T &self(T &t) { return t;  }
template<typename T> struct Dependent {  };

template<typename T>
class Synchronised : Dependent<T>{
 public:
  explicit Synchronised(T t = T()) : t_(t) {}
  template<typename Functor>
  auto operator()(Functor functor) const ->decltype(functor(self(*this).t_)) {
  //auto operator()(Functor functor) const ->decltype(functor(this->t_)) {
    std::lock_guard<std::mutex> lock(mutex_);
    return functor(t_);
  }
 private:
  mutable T t_;
  mutable std::mutex mutex_;
};


int main() {

    Synchronised<std::string> sync_string("Start\n");
    std::vector<std::future<void>> futures;
}
dirvine
  • 57,399
  • 2
  • 18
  • 19

1 Answers1

4

The below was only sufficient for making the class template definition itself valid. However the same rules that made the lookup not find the data member in the class template (which necessiated the introduction of the empty dependent base class or the dependent function call) will also make the instantiation of the class template not find the data member, and thereby will trigger a compiler error.

We told the compiler "hold on, perhaps you will find the data member at instantiation time", but I did not think about what will happen when actually instantiating. We will now make it so that the name is still dependent even after instantiation of the class happened. The resolution will have to wait until the call to operator().

// keep this little util somewhere :)
template<typename T>
struct self { 
  template<typename U> U &operator()(U &t) { return t; } 
};

template <class T>
class Synchronised {
    public:
// ...
        auto operator()(F f) const -> decltype(f(self<F>()(*this).t_)) {
// ...
};

The use of a class template for self instead of a function template will also prevent argument dependent lookup from happening, preventing that the author of F also writes a function called self that matches the argument *this (this could have been a potential problem with the partial solution below, too).


You have several other options, beside reordering

  1. Making the expression on the left side of . dependent, but not just the enclosing class (because it will be special-cased)

    // keep this little util somewhere :)
    template <class T> T &self(T &t) { return t; }
    
    template <class T>
    class Synchronised {
        public:
    // ...
            auto operator()(F f) const -> decltype(f(self(*this).t_)) {
    // ...
    };
    
  2. Introduce a dependent base class to work-around the special casing of the enclosing class

    // Keep this little util somewhere
    template<typename T> struct Dependent { };
    
    template <class T>
    class Synchronised : Dependent<T> {
        public:
    // ...
            auto operator()(F f) const -> decltype(f(this->t_)) {
    // ...
    };
    

The first is based on the Standard making self(*this).t_ a member of an unknown specialization

  • the type of the object expression is dependent and is not the current instantiation.

The second is based on the Standard making this->t_ a member of an unknown specialization

  • the type of the object expression is the current instantiation, the current instantiation has at least one dependent base class, and name lookup of the id-expression does not find a member of the current instantiation or a non-dependent base class thereof;

This in turn makes x->t_ for both cases a dependent expression and hence the name will be looked up at instantiation time. The Standard says

A class member access expression (5.2.5) is type-dependent if the expression refers to a member of the current instantiation and the type of the referenced member is dependent, or the class member access expression refers to a member of an unknown specialization.

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
  • I had thought this was the answer Johannes, but I have tried both attempts and still require to have the private: before the public: in both your's and Howard's suggestion. This seems to be true in clang and gcc (3.2 and 4.7.2 respectively). It seems like it fits the bill and should work but ... Any help appreciated on this one. – dirvine Jan 14 '13 at 17:15
  • @dirvine well it appears to me that it is a clang bug then. however i could be wrong ofc. so i recommend you to open a bug report and see what they say. – Johannes Schaub - litb Jan 14 '13 at 17:17
  • @ Johannes I will do, it does seem to be the case in gcc as well though, do you think I should report this to both clang and gcc ? – dirvine Jan 14 '13 at 17:26
  • @dirvine yes, if you want. although i think they should be aware of this and just havent come around implementing it. – Johannes Schaub - litb Jan 14 '13 at 21:52
  • @ Johannes I am marking this as the answer unless somebody spots differently this would appear to be the correct implementation (bugs aside). Thanks again and sorry for all the confusion (too many jobs not enough minutes). – dirvine Jan 14 '13 at 23:25
  • @dirvine can you please post the bug url if you happen to file one? – Johannes Schaub - litb Jan 16 '13 at 07:53
  • http://gcc.gnu.org/bugzilla/show_bug.cgi?id=56004 Only just now added as I was attempting to see where I got with clang. I recieved this so far but am not sure it's a correct – dirvine Jan 16 '13 at 15:15
  • From clang mailing list = 'This is ill-formed, since t_ has not yet been declared when the trailing-return-type for operator() is processed. Per [basic.lookup.classref]p2, the name t_ is looked up in the scope of class Synchronized. Per [basic.scope.class]p1, the name is not in scope in the trailing-return-type, because it is not in the declarative region following the name's point of declaration, and isn't a function body, default argument, nor non-static data member initializer.' – dirvine Jan 16 '13 at 15:17
  • @ Johannes Yes Johnathen Wakely over on the gcc list has the same (correct) opinion. Great if you can update the answer and I will select it. It still stand though and yes my badly worded question is this the only situation where we have to move the private: before public: I realise this is for declaration before use but it does mean moving the parts of the class around so where code standards say public: then private: it seems safe to say so, in this case it's not. Thanks again though Johannes for all the tips. – dirvine Jan 16 '13 at 18:53
  • What I mean is that this may be a case where c++11 introduced something (decltype trailing return that accesses a class member) that forces the order to ensure declarations happen before use. – dirvine Jan 16 '13 at 18:55
  • 1
    @dirvine true. although that was the case in c++03 too, but now it happens in one particularly nasty situation, because trailing return types often seem to access privates :) for example, this has failed in c++03 too, but works if you swap the order: `struct A { Foo f(); private: typedef int Foo; };`. I guess people are used to having the full set of members of they say `foo.bar`, but now `foo` will only have a partial set of members :) – Johannes Schaub - litb Jan 16 '13 at 22:32