Clang 访问修饰符顺序和 decltype

Posted

技术标签:

【中文标题】Clang 访问修饰符顺序和 decltype【英文标题】:Clang access modifier order and decltype 【发布时间】:2013-01-07 01:03:57 【问题描述】:

我一直在考虑创建一个同步器帮助器模板类,它基于 Herb Sutter 在这个 talk 中的包装器类的想法这在 msvc 中不起作用(除非我们删除大括号初始化)但是当大括号初始化时被删除然后没关系。

在 clang/gcc (ubuntu 12.10, gcc4.7.2, clang (3.2) selfbuilt 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> lockmutex_;
            return f(t_);
        
        private: // place this before public: and this object compiles
            mutable T t_;
            mutable std::mutex mutex_;
;

编辑:添加 Johannes 的想法和完整的课程,以防有人想要剪切和粘贴。

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

【问题讨论】:

向霍华德道歉,我现在已将正确答案移至约翰内斯版本。使用 clang 3.2 实际上它确实在这方面出错,所以现在看来​​它不是未定义的行为,我认为这是个好消息。 确诊不代表不是UB @Johannes 正确但不编译是一个好兆头,或者至少在正确的方向上) 【参考方案1】:

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

我们告诉编译器“等等,也许你会在实例化时找到数据成员”,但我没有想到实际实例化时会发生什么。我们现在将使它的名称仍然依赖,即使在类的实例化发生之后。解决方案必须等到致电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-&gt;t_成为未知专业化成员的标准

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

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

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

【讨论】:

我原以为这是约翰内斯的答案,但我已经尝试了两种尝试,但仍然需要在你和霍华德的建议中拥有私人:在公众面前:这在 clang 和 gcc(分别为 3.2 和 4.7.2)中似乎是正确的。看起来它符合要求并且应该可以工作,但是......对此有任何帮助。 @dirvine 好吧,在我看来,这是一个铿锵的错误。但是我可能是错的。所以我建议你打开一个错误报告,看看他们怎么说。 @Johannes 我会的,不过在 gcc 中似乎也是如此,你认为我应该向 clang 和 gcc 报告这个吗? @dirvine 是的,如果你愿意的话。虽然我认为他们应该意识到这一点,只是还没有来实施它。 @dirvine 是真的。尽管在 c++03 中也是如此,但现在它发生在一种特别讨厌的情况下,因为尾随返回类型似乎经常访问私有 :) 例如,这在 c++03 中也失败了,但如果你交换订单:struct A Foo f(); private: typedef int Foo; ;。我猜人们已经习惯了他们说foo.bar 的完整成员集,但现在foo 将只有部分成员集:)

以上是关于Clang 访问修饰符顺序和 decltype的主要内容,如果未能解决你的问题,请参考以下文章

staticfinal包访问修饰符内部类

如何告诉 clang-format 缩进可见性修饰符?

在Java和c#中如果不写访问修饰符,类和类成员默认的是啥访问修饰符?

在Java和c#中如果不写访问修饰符,类和类成员默认的是啥访问修饰符?

成员变量和成员方法的访问控制修饰符都有哪些?

Java 方法(变量)修饰符的使用顺序