这更像是一个c ++标准问题.请考虑以下代码:
template <typename T>
class has_Data
{
typedef char one;
typedef long two;
template <typename C> static one test( typeof(&C::Data) ) ;
template <typename C> static two test(...);
public:
enum { value = sizeof(test<T>(0)) == sizeof(char) };
};
class MyClass {
private:
struct Data {
};
};
void function(bool val = has_Data<MyClass>::value) {}
Run Code Online (Sandbox Code Playgroud)
上面的代码适用于 gcc (GCC) 4.4.3
然而 clang version 3.3 (2545b1d99942080bac4a74cda92c620123d0d6e9) (2ff97832e593926ea8dbdd5fc5bcf367475638a9)
它给出了这个错误:
test_private_data.cpp:7:54: error: 'Data' is a private member of 'MyClass'
template <typename C> static one test( typeof(&C::Data) ) ;
^
/devshared/home/rhanda/test_private_data.cpp:7:37: note: while substituting explicitly-specified template arguments into function template 'test'
template <typename C> static one test( typeof(&C::Data) ) ;
^
/devshared/home/rhanda/test_private_data.cpp:21:26: note: in instantiation of template class 'has_Data<MyClass>' requested here
void function(bool val = has_Data<MyClass>::value) {}
^
1 error generated.
Run Code Online (Sandbox Code Playgroud)
哪一个是对的?
从标准文件(n3485),我发现一个声明似乎比gcc更赞同clang.
访问控制统一应用于所有名称,无论名称是从声明还是表达式引用.
我认为海湾合作委员会是对的。
\n\n首先要注意的是,没有非friend代码都不应能够积极报告给定私有成员的存在。因此,如果您想这样做,则必须修改您的设计。一个类可以用它的私有成员做任何事情,其他代码(除了朋友)应该没有办法知道它。这是设计使然。
不过,有一个SFINAE原则:替换失败不是错误。由于MyClass::Data是私有的,所以我认为 \xe2\x80\x93 中的代码应该像根本has_Data没有成员一样。C::Data因此,第一个函数会导致替换失败,该失败会被默默地忽略,而第二个函数就是所使用的函数。添加更多代码,我的 GCC 4.7.2 可以毫无问题地编译它,并has_Data<MyClass>::value评估为false. 我认为 SFINAE 是正确的。
试图引用您提到的文件中的内容来支持这一观点,我在第 14.8.2 节第 8 段中发现了以下内容:
\n\n\n\n\n注意:访问检查是作为替换过程的一部分完成的。
\n
这是标准中的非规范性注释,但对我来说似乎是一个非常可读且清晰的指示,表明 SFINAE 实际上应该适用于这种情况,就像 GCC 处理它的方式一样。
\n\n编辑:正如@hvd在评论中指出的那样,上述内容仅适用于 C++11。在旧版本的标准中,情况有所不同。问题 1170:模板参数推导期间的访问检查包含有关该更改的详细信息。
\n\nGCC 不会因为它是 GNU 扩展-std=c++03而编译此代码。仍然编译代码的事实可能被认为是不合适的,但由于前进的方向是使用 C++11 语义,所以我不会费心提交有关此的报告。-std=c++11typeof-std=gnu++03