我一直在寻找创建一个同步器帮助器模板类,它基于Herb Sutter关于此talk中包装器类的想法。这在msvc中不起作用(除非我们删除了大括号初始化)但是当括号初始化被删除然后它没关系。
在clang / gcc(ubuntu 12.10,gcc4.7.2,clang(3.2)中用libc ++自构建)似乎私有访问修饰符必须出现在public之前:这看起来有点奇怪。
gcc中的错误是
error: ‘t_’ was not declared in this scope
和clang是
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;
}
答案 0 :(得分: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
(这可能是下面的部分解决方案的潜在问题)。
除了重新排序
之外,还有其他几个选项使.
左侧的表达式依赖,但不仅仅是封闭类(因为它将是特殊的)
// 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_)) {
// ...
};
引入依赖基类来处理封闭类的特殊外壳
// 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)是类型相关的。未知的专业化。