我正在尝试在编译时选择要使用的类型,具体取决于某个类型是否在给定范围内公开可用。最好直接进入代码:
#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();
}
}
我的目的是在A
中,有Log
类型,因此应该使用而不是全局::Log
,而B
则没有,它应该使用全局::Log
。现在这两个都不管1.
(我的错误测试,以查看该范围中的类型是私有),无论如何......
问题出在C
和D
,通常情况下 - 如果没有测试,Log::log()
会失败,因为它在A
中是私有的。但是,如果使用std::conditional<>
,则不会出现编译错误,并且输出不正确,因为它以A:
为前缀。所以,我错过了什么(除了不正确的测试 - 我需要以某种方式修复......)?如果没有,那么这种方法是使用A
法律在std::conditional
中公开私有类型吗?
编辑:为了理智,我测试了以下内容:
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)
{
{
// 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
我暂时将这个问题保持开放......稍后将以上述内容结束。
答案 0 :(得分:1)
我已经修改了你的例子,所以w / my gcc 4.8.1现在一切都按预期工作了(预期)。
关于原始代码的几点说明:
Log
(使用use_logger
)的辅助功能时,use_logger
是A
的外部类的主要误解,B
,C
,D
!它不能(按设计)访问该类的任何'cept public 成员! Log
传递给它,你将松开“上下文” - 即检查器不知道(并且它没有办法实现它与那个设计)“这种类型实际上是其他东西的嵌套类型?”use_logger
只是错误:始终将0
重新解释为P*
- 此代码没有其他可能性(解释方式)。 ..这类检查器背后的主要思想是形成一组“匹配”函数,然后,在实例化时,编译器将通过SFINAE删除不适当的(以及“回退”到通用test(...)
重载)或接受一些最适合结果过载集。您的test(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 << ' ' << 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.