解决基模板类成员的模糊性

Resolve ambiguousness of a base template class member

本文关键字:成员 模糊性 解决      更新时间:2023-10-16

我有一个类的层次结构,每个类都必须有一个特定的基类。基类提供了发布日志记录的能力,并接受日志通道的tor名称(基本上是使用log的类的名称)。我们称这个类为Logable

为了允许我的类多次继承Logable类,我给了它一个模板参数,每个后代都使用自己作为这个参数。

实际上我使用的是boost::log库,但有一个非常简化的例子,说的层次结构与简单的LogableImpl类,这是取代boost::log sink。

#include <iostream>
#include <string>
// macro for logging in a boost::log style
#define LOG_DEBUG this->_loggerObj.logStream("debug")
#define LOG_INFO  this->_loggerObj.logStream("info")
#define LOG_WARN  this->_loggerObj.logStream("warning")
#define LOG_ERROR this->_loggerObj.logStream("error")

class LogableImpl
{
private:
   std::string _channelName;
public:
   LogableImpl(const std::string & channelName): _channelName(channelName) {}
   std::ostream & logStream(const std::string & severetyLevel)
   {
      std::cout << _channelName << " " << severetyLevel;
      return std::cout;
   }
};

template <class Descendant>
class Logable
{
protected:
   Logable(const std::string & channelName): _loggerObj(channelName) {}
   LogableImpl _loggerObj;
};

class Base: private Logable<Base>
{
public:
   Base()
      : Logable<Base>("Base")
   {}
   void someMethod()
   {
      LOG_INFO << "some method is called" << std::endl;
      LOG_ERROR << "an error happened" << std::endl;
   }
};

class Derived: public Base, private Logable<Derived>
{
public:
   Derived()
      : Logable<Derived>("Derived")
   {}
   void someAnotherMethod()
   {
      LOG_INFO << "another method is called" << std::endl;
      LOG_ERROR << "another error is happened" << std::endl;
   }
};

int main()
{
   Base b;
   Derived d;
   b.someMethod();
   d.someMethod();
   return 0;
}

显然,我在使用MSVC 2008编译此源代码时出现了错误

error C2385: ambiguous access of '_loggerObj'
1>        could be the '_loggerObj' in base 'Logable<Base>'
1>        or could be the '_loggerObj' in base 'Logable<Derived>'
1>d:cppvisualstudiotestsworkbenchtestmain.cpp(55) : error C2248: 'Logable<Descendant>::_loggerObj' : cannot access inaccessible member declared in class 'Logable<Descendant>'
1>        with
1>        [
1>            Descendant=Base
1>        ]
1>        d:cppvisualstudiotestsworkbenchtestmain.cpp(29) : see declaration of 'Logable<Descendant>::_loggerObj'
1>        with
1>        [
1>            Descendant=Base
1>        ]
1>d:cppvisualstudiotestsworkbenchtestmain.cpp(56) : error C2385: ambiguous access of '_loggerObj'
1>        could be the '_loggerObj' in base 'Logable<Base>'
1>        or could be the '_loggerObj' in base 'Logable<Derived>'
1>d:progcppvisualstudiotestsworkbenchboost_testmain.cpp(56) : error C2248: 'Logable<Descendant>::_loggerObj' : cannot access inaccessible member declared in class 'Logable<Descendant>'
1>        with
1>        [
1>            Descendant=Base
1>        ]
1>        d:cppvisualstudiotestsworkbenchtestmain.cpp(29) : see declaration of 'Logable<Descendant>::_loggerObj'
1>        with
1>        [
1>            Descendant=Base
1>        ]
1>Build log was saved at "file://d:cppvisualStudiotestsworkbenchtestDebugBuildLog.htm"
1>boost_test - 4 error(s), 0 warning(s)
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

如何在LOG_*宏中指定正确的基本成员?我觉得这可以用一些模板魔法来完成,但就是想不明白。

必须使用MSVC2008完成,它不支持c++ 11x的特性

我使用c++11通过显式指定应该使用哪个Logable来做到这一点。由于我们不知道this的类型,我使用decltype:

#define LOGABLE_TYPE typename std::remove_reference<decltype(*this)>::type
#define LOG_DEBUG this->Logable<LOGABLE_TYPE>::_loggerObj.logStream("debug")
#define LOG_INFO  this->Logable<LOGABLE_TYPE>::_loggerObj.logStream("info")
#define LOG_WARN  this->Logable<LOGABLE_TYPE>::_loggerObj.logStream("warning")
#define LOG_ERROR this->Logable<LOGABLE_TYPE>::_loggerObj.logStream("error")

查看完整代码:http://ideone.com/1D5jrj

不幸的是,我无法找到一种方法将decltype替换为msvc 2008可以理解的东西,以使用peter的答案。即使boost::typeof也不合适(只要我正确使用它)

所以我来了一个解决方案,通过添加一个using案例与宏

#include <iostream>
#include <string>
#define USE_APPROPRIATE_LOGGER(classname) using Logable<classname>::_loggerObj
#define LOG_DEBUG _loggerObj.logStream("debug")
#define LOG_INFO  _loggerObj.logStream("info")
#define LOG_WARN  _loggerObj.logStream("warning")
#define LOG_ERROR _loggerObj.logStream("error")
class LogableImpl
{
private:
   std::string _channelName;
public:
   LogableImpl(const std::string & channelName): _channelName(channelName) {}
   std::ostream & logStream(const std::string & severetyLevel)
   {
      std::cout << _channelName << " " << severetyLevel << " ";
      return std::cout;
   }
};

template <class Descendant>
class Logable
{
protected:
   Logable(const std::string & channelName): _loggerObj(channelName) {}
   LogableImpl _loggerObj;
};
class Base: private Logable<Base>
{
   USE_APPROPRIATE_LOGGER(Base);
public:
   Base()
      : Logable<Base>("Base")
   {}
   void someMethod()
   {
      LOG_INFO << "some method is called" << std::endl;
      LOG_ERROR << "an error happened" << std::endl;
   }
};
class Derived: public Base, private Logable<Derived>
{
   USE_APPROPRIATE_LOGGER(Derived);
public:
   Derived()
      : Logable<Derived>("Derived")
   {}
   void someAnotherMethod()
   {
      LOG_INFO << "another method is called" << std::endl;
      LOG_ERROR << "another error is happened" << std::endl;
   }
};

int main()
{
   Base b;
   Derived d;
   b.someMethod();
   std::cout << std::endl;
   d.someAnotherMethod();
   return 0;
}

很丑,并且扼杀了使用继承来为类提供可记录性的想法,但似乎没有其他方法可以使用c++11