可以通过模板间接访问基类中的私有类型 [英] Possible to access private types in base classes via template indirection

查看:193
本文介绍了可以通过模板间接访问基类中的私有类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图在编译时根据是否在给定范围内公开提供,选择要使用的类型。最好直接转到代码:

  #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<< ''<<消息< 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()。
}
};

class B
{
public:

void say()
{
std :: cout< B:<< use_logger< Log> :: value<< std :: endl;
std :: conditional< use_logger< Log> :: value,Log,:: Log> :: type :: log()。
}
};

class C:A
{
public:

void say()
{
std :: cout< < C:< use_logger< Log> :: value<< std :: endl;
// 2。
std :: conditional< use_logger< Log> :: value,Log,:: Log> :: type :: log()。
// 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,:: 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 ,所以应该使用而不是全局 :: Log ,并且在 B 中没有,应该使用全局 :: Log 。现在这两个工作不管 1。(我不正确的测试,看看类型是私人在这个范围..)



问题出现在 C D Log :: log()失败,因为它在 A 中是私有的。但是,如果使用 std :: conditional<> ,则没有编译错误,输出不正确,因为它以 A作为前缀: 。所以,我错过了什么(除了不正确的测试 - 我需要以某种方式修复...)?如果没有,那么这种方法是使用 std :: conditional legal?来暴露 A p>

编辑:为了理智,我测试了以下:

  std: :conditional< false,Log,:: Log> :: type :: log(From C); 
std :: conditional< false,Log,:: Log> :: type :: log(From D);

它使用全局 :: Log ,如果是真的,它是以某种方式使用私人 A :: Log



EDIT2:是一个更一般的条件,即你可以通过模板间接访问一些内部私有类型,例如:

  class F 
{
struct Foo
{
void bar(){}
};
};

template< typename T>
struct ExposeInternal
{
typedef T type;
};

int main(void)
{
{
//我们有Foo!
ExposeInternal< F :: Foo> :: type t;
t.bar();
}
{
//下面失败的
F :: Foo t;
t.bar();
}
}

编辑3:好 - 已确认,报告GCC错误,与 std :: conditional 无关,尚未在4.7或4.8中修复。 http://gcc.gnu.org/bugzilla/show_bug.cgi?id=47346



我将暂时打开此问题..将在以后关闭它。

解决方案

我已经修改了你的例子,所以w /我的gcc 4.8.1现在一切工作按预期(预期)。



当您想测试日志


  • c $ c>(使用 use_logger )主要误解为 use_logger 外部 A B C D !它不能(通过设计)访问该类的任何东西 public 成员!

  • 您的检查程序的第二个方面:传递类型日志到它,你将松开上下文不知道(和它没有办法实现它w /那个设计)是这种类型实际上是一个嵌套类型的东西

  • 最后 use_logger 刚刚不正确:始终重新解释 0 P * - 没有其他可能性(解释的方式)这个代码...这种检查器的主要想法是形成一组匹配函数,然后,在实例化时,编译器将通过SFINAE(和fallback到通用的测试(...) overload)删除不适当的,或者从结果重载集合中接受一些最合适的。您的测试(P *)只是始终相关! - 这是为什么它不选择任何实际上...



所以,这里是我的代码:

  #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<< ''<<消息< std :: endl;
}
};

struct Log
{
static Logger& log()
{
static Logger _def(Default:);
return _def;
}
};

命名空间详细信息{
///用于检查嵌套类型的可用性的辅助类\c Log
/// whithing \c T和它的静态函数\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)是嵌套类型\c日志访问
模板< 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我想原始代码在这里有一个拼写错误:
///在\c私有段中的任何东西继承将
/// \b无法访问具有\c所有种类的继承的孩子!
///所以如果我们想从\c D使用它,它\b必须至少是
/// \c protected。
protected:
struct Log
{
static Logger& log()
{
static Logger _def(A:);
return _def;
}
};

/// \attention检查器和访问器\c必须是此类的朋友。
///从\c A :: say(它实际上是一个成员,所以它
///有完全访问其他成员)调用Cuz,它必须有\b相同访问
///作为其他(说)成员!
friend struct details :: has_nested_logger_available_checker;
/// \todo合并(实际)检查器和存取器到同一个类到
///减少代码类型...(一点)
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);
}
};

///使用\c public inharitance,\c D可以访问\c public和/或\c protected
///成员\c A.但是不是\c私人!
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:从A
B:0
默认值:从B
C:0
默认值:从C
D:1
A:从D

zaufi @ gentop / work / tests $ g ++ --version
g ++(Gentoo 4.8.1 p1.0,pie-0.5.6)4.8。 1
版权所有(C)2013自由软件基金会,
这是免费软件
查看复制条件的来源。有NO
保修,
甚至不适用于适销性或特定用途的适用性。


I'm trying to, at compile time, select a type to use depending on whether one is publicly available in a given scope. It's best to go straight to the code:

#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, ::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, ::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, ::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();
  }
}

My intention is that in A, there is a type Log, so that should be used rather than the global ::Log, and in B where there is none, it should use the global ::Log. Now both these work irrespective of 1. (my incorrect test to see if the type is private in this scope..)

The problem is in C and D, normally - without the test, Log::log() fails, because it's private in A. However if the std::conditional<> is used, there is no compilation error, and the output is incorrect as it is prefixed with A:. So, what have I missed (apart from the incorrect test - which I need to somehow fix...)? If nothing, then is this approach of exposing the private type in A using the std::conditional legal?

EDIT: for sanity, I tested with the following:

std::conditional<false, Log, ::Log>::type::log("From C");
std::conditional<false, Log, ::Log>::type::log("From D");

And it does use the global ::Log, if it's true, it's somehow using the private A::Log.

EDIT2: Infact this appears to be a more general condition, i.e. you can easily get access to some internal private types via a template indirection, e.g:

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();
  }
}

EDIT 3: Okay - have confirmed, it is a reported GCC bug, nothing to do with std::conditional, not yet fixed in 4.7 or 4.8. http://gcc.gnu.org/bugzilla/show_bug.cgi?id=47346

I will leave this question open for the moment.. will close it with the above later.

解决方案

I've modified your example a little, so w/ my gcc 4.8.1 now everything work as expected (intended).

Few notes about original code:

  • when you wanted to test accessibility of a Log (using use_logger) the primary misunderstanding that use_logger is an external class for A, B, C, D! It can't (by design) to access anything 'cept public members of that classes!
  • the second aspect about your checker: passing the type Log into it, you going to loose "context" -- i.e. the checker doesn't know (and it have no way to realize it w/ that design) "is this type is actually a nested type of something else?"
  • and finally use_logger just incorrect: it always reinterpret 0 to a P* -- there is no other possibilities (ways to interpret) this code... the main idea behind such checkers is to form a set of "matched" functions, then, at instantiation time, compiler will remove inappropriate by SFINAE (and "fallback" to a generic test(...) overload) or accept some as most suitable from result overload set. Your test(P*) just always relevant! -- It is why it doesn't choose anything actually...

so, here is my code:

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

the output:

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.

这篇关于可以通过模板间接访问基类中的私有类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆