4

我一直在考虑创建一个同步器帮助器模板类,它基于 Herb Sutter 在本次演讲中关于包装器类的想法这在 msvc 中不起作用(除非我们删除大括号初始化)但是当大括号初始化被删除时就可以了.

在 clang/gcc (ubuntu 12.10, gcc4.7.2, clang (3.2) self built with libc++) 中,私有访问修饰符似乎必须出现在公众面前:这似乎有点奇怪。

gcc 中的错误是 error: ‘t_’ was not declared in this scope

铿锵是

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

这可能是我不知道的模板/declytpe 问题,并且想知道是否有人可以帮助解决这个问题。(全部使用相关的 c++11 标志编译)

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_;
};

编辑:添加约翰内斯的想法和全班,以防有人想要剪切和粘贴。

#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;
}
4

1 回答 1

4

以下仅足以使类模板定义本身有效。然而,使查找在类模板中找不到数据成员的相同规则(这需要引入空的依赖基类或依赖函数调用)也会使类模板的实例化找不到数据成员,并且从而会触发编译器错误。

我们告诉编译器“等等,也许你会在实例化时找到数据成员”,但我没有想到实际实例化时会发生什么。我们现在将使它的名称仍然依赖,即使在类的实例化发生之后。决议将不得不等到调用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_)) {
// ...
};

使用类模板self代替函数模板也将防止发生依赖于参数的查找,从而防止作者F还编写一个self与参数匹配的函数调用*this(这也可能是下面部分解决方案的潜在问题)。


除了重新排序之外,您还有其他几个选择

  1. 在依赖的左侧制作表达式.,但不仅仅是封闭类(因为它将是特殊情况)

    // 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. 引入一个依赖基类来解决封闭类的特殊外壳

    // 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_)) {
    // ...
    };
    

第一个是基于标准使self(*this).t_未知专业化的成员

  • 对象表达式的类型是依赖的,不是当前的实例化。

第二个是基于标准制作this->t_一个未知专业的成员

  • 对象表达式的类型为当前实例化,当前实例化至少有一个依赖基类,id-expression的名称查找没有找到当前实例化的成员或其非依赖基类;

这反过来又使x->t_这两种情况都成为依赖表达式,因此将在实例化时查找名称。标准说

如果表达式引用当前实例化的成员并且被引用成员的类型是依赖的,或者类成员访问表达式引用未知特化的成员,则类成员访问表达式 (5.2.5) 是类型相关的。

于 2013-01-13T23:10:24.143 回答