c – 可以通过模板间接访问基类中的私有类型
我正在尝试在编译时选择要使用的类型,具体取决于是否在给定范围内公开可用.最好直接进入代码:
#include <iostream> #include <type_traits> class Logger { std::string _p; public: Logger(std::string p): _p(p) { } void say(std::string message) { std::cout << _p << ' ' << message << std::endl; } }; struct Log { static Logger& log() { static Logger _def("Default: "); return _def; } }; // 1. template <typename P> struct use_logger { static std::size_t test(P*); static char test(...); static const bool value = sizeof(test(reinterpret_cast<P*>(0))) == sizeof(std::size_t); }; class A { struct Log { static Logger& log() { static Logger _def("A: "); return _def; } }; public: void say() { std::cout << "A: " << use_logger<Log>::value << std::endl; std::conditional<use_logger<Log>::value,Log,::Log>::type::log().say("From A"); } }; class B { public: void say() { std::cout << "B: " << use_logger<Log>::value << std::endl; std::conditional<use_logger<Log>::value,::Log>::type::log().say("From B"); } }; class C : A { public: void say() { std::cout << "C: " << use_logger<Log>::value << std::endl; //2. std::conditional<use_logger<Log>::value,::Log>::type::log().say("From C"); // Log::log().say("From C"); } }; class D : public A { public: void say() { // 2. std::cout << "D: " << use_logger<Log>::value << std::endl; std::conditional<use_logger<Log>::value,::Log>::type::log().say("From D"); // Log::log().say("From C"); } }; int main(void) { { A i; i.say(); } { B i; i.say(); } { C i; i.say(); } { D i; i.say(); } } 我的意图是在A中,有一个类型的Log,所以应该使用而不是global :: Log,而在B中没有,它应该使用global :: Log.现在这两个工作都不管1.(我的错误测试,看看这个范围内的类型是否是私有的..) 问题出在C和D中,通常 – 没有测试,Log :: log()失败,因为它在A中是私有的.但是如果std :: conditional<>使用,没有编译错误,输出不正确,因为它以A:为前缀.所以,我错过了什么(除了不正确的测试 – 我需要以某种方式修复……)?如果没有,那么这种方法是使用std :: conditional legal暴露A中的私有类型吗? 编辑:为了理智,我测试了以下内容: std::conditional<false,::Log>::type::log("From C"); std::conditional<false,::Log>::type::log("From D"); 它确实使用了global :: Log,如果它是真的,它会以某种方式使用私有的A :: Log. EDIT2:事实上,这似乎是一个更一般的条件,即您可以通过模板间接访问轻松访问某些内部私有类型,例如: class F { struct Foo { void bar() { } }; }; template <typename T> struct ExposeInternal { typedef T type; }; int main(void) { { // We've got Foo! ExposeInternal<F::Foo>::type t; t.bar(); } { // Below fails F::Foo t; t.bar(); } } 编辑3:好的 – 已经确认,这是一个报告的GCC错误,与std :: conditional无关,尚未在4.7或4.8中修复. http://gcc.gnu.org/bugzilla/show_bug.cgi?id=47346 我暂时将这个问题保持开放……稍后将关闭它. 解决方法
我已经修改了你的例子,所以w / my gcc 4.8.1现在一切都按预期工作(预期).
关于原始代码的几点注释: >当你想测试Log的可访问性(使用use_logger)时,主要的误解是use_logger是A,B,C,D的外部类!它不能(通过设计)访问任何’cept公共成员的那些类! 所以,这是我的代码: #include <iostream> #include <string> #include <type_traits> #include <boost/mpl/eval_if.hpp> #include <boost/mpl/identity.hpp> class Logger { std::string _p; public: Logger(std::string p): _p(p) { } void say(std::string message) { std::cout << _p << ' ' << message << std::endl; } }; struct Log { static Logger& log() { static Logger _def("Default: "); return _def; } }; namespace details { /// Helper class to check availability of a nested type c Log /// whithing c T and it's static function c log() struct has_nested_logger_available_checker { typedef char yes_type; typedef char (&no_type)[2]; template <typename T> static no_type test(...); template <typename T> static yes_type test( typename std::add_pointer< decltype(std::is_same<decltype(T::Log::log()),Logger>::value,void()) >::type ); }; } /// Metafunction (type trait) to check is a nested type c Log accessible template <typename T> struct has_nested_logger_available : std::is_same< decltype(details::has_nested_logger_available_checker::template test<T>(nullptr)),details::has_nested_logger_available_checker::yes_type > {}; template <typename T> struct access_nested_logger { typedef typename T::Log type; }; template <typename T> struct logger_chooser : public boost::mpl::eval_if< has_nested_logger_available<T>,access_nested_logger<T>,boost::mpl::identity<::Log> > { }; class A { /// attention I suppose original code has a typo here: /// anything in a c private section being inherited will be /// b inaccessible to a child with c all kind of inheritance! /// So if latter we want to use it from c D,it b must be at least /// c protected. protected: struct Log { static Logger& log() { static Logger _def("A: "); return _def; } }; /// attention Checker and accessor c MUST be a friend of this class. /// Cuz being called from c A::say (which is actually a member,so it /// has full access to other members),it must have b the same access /// as other (say) member(s)!!! friend struct details::has_nested_logger_available_checker; /// todo Merge (actual) checker and "accessor" to the same class to /// reduce code to type... (a little) friend struct access_nested_logger<A>; public: void say() { std::cout << "A: " << has_nested_logger_available<A>::value << std::endl; logger_chooser<A>::type::log().say("From A"); } }; class B { public: void say() { std::cout << "B: " << has_nested_logger_available<B>::value << std::endl; logger_chooser<B>::type::log().say("From B"); } }; class C : A { public: void say() { std::cout << "C: " << has_nested_logger_available<C>::value << std::endl; logger_chooser<C>::type::log().say("From C"); } }; /// With c public inharitance,c D can access c public and/or c protected /// members of c A. But not c private !!! class D : public A { public: /// sa c A friend struct details::has_nested_logger_available_checker; friend struct access_nested_logger<D>; void say() { std::cout << "D: " << has_nested_logger_available<D>::value << std::endl; logger_chooser<D>::type::log().say("From D"); } }; int main(void) { { A i; i.say(); } { B i; i.say(); } { C i; i.say(); } { D i; i.say(); } return 0; } 输出: zaufi@gentop /work/tests $g++ -std=c++11 -o so_log_test so_log_test.cc zaufi@gentop /work/tests $./so_log_test A: 1 A: From A B: 0 Default: From B C: 0 Default: From C D: 1 A: From D zaufi@gentop /work/tests $g++ --version g++ (Gentoo 4.8.1 p1.0,pie-0.5.6) 4.8.1 Copyright (C) 2013 Free Software Foundation,Inc. This is free software see the source for copying conditions. There is NO warranty not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |