Я пытался создать класс шаблона помощника синхронизатора, основанный на идеях Херба Саттера о классе-оболочке в этом говорить Это не работает в msvc как есть (если мы не удалим инициализацию фигурной скобки), но когда инициализация фигурной скобки удалена, тогда это нормально.
В clang / gcc (ubuntu 12.10, gcc4.7.2, clang (3.2), собранный с помощью 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;
}
Приведенного ниже было достаточно только для того, чтобы сделать само определение шаблона класса действительным. Однако те же правила, которые заставили поиск не находить член данных в шаблоне класса (что требовало введения пустого зависимого базового класса или вызова зависимой функции), также приведут к тому, что экземпляр шаблона класса не найдет член данных, и тем самым вызовет ошибку компилятора.
Мы сказали компилятору: «Держись, возможно, ты найдешь элемент данных во время создания экземпляра», но я не думал о том, что произойдет при создании экземпляра. Теперь мы сделаем так, чтобы имя все еще зависело даже после того, как произошло создание экземпляра класса. Решение придется ждать до звонка 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-выражения не находит члена текущего экземпляра или его независимого базового класса;
Это в свою очередь делает x->t_
для обоих случаев зависимое выражение и, следовательно, имя будут найдены во время создания экземпляра. Стандарт говорит
Выражение доступа к члену класса (5.2.5) зависит от типа, если выражение относится к члену текущего экземпляра и тип ссылочного члена является зависимым, или выражение доступа к члену класса относится к члену неизвестной специализации.
Других решений пока нет …