非实例化模板成员的编译时错误而不是链接时错误 [英] Compile-time error for non-instantiated template members instead of link-time error

查看:26
本文介绍了非实例化模板成员的编译时错误而不是链接时错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有模板类 ItemContainer,它实际上是具有不同功能(如排序、索引、分组等)的整个容器系列的外观.

I have template class ItemContainer that actually is facade for a whole family of containers with different capabilities like sorting, indexing, grouping etc.

使用 pimpl 惯用语和显式实例化将实现细节隐藏在 cpp. 文件中.模板仅使用定义容器实际行为的一组众所周知的有限实现类进行实例化.

Implementation details are hidden in cpp. file using pimpl idiom and explicit instantiation. Template is instantiated only with well-known limited set of implementation classes that define the actual behavior of container.

主模板实现了所有容器支持的常用函数 - IsEmpty()GetCount()Clear()

Main template implements common functions supported by all containers - IsEmpty(), GetCount(), Clear() etc.

每个特定的容器都专门提供一些仅由它支持的功能,例如Sort() 用于排序容器,operator[Key&] 用于键索引容器等

Each specific container specializes some functions that are supported only by it, e.g. Sort() for sorted container, operator[Key&] for key indexed container etc.

这种设计的原因是 class 替代了一些在 90 世纪初由一些史前人编写的传统手工制作的自行车容器.想法是用现代 STL&Boost 容器替换旧的腐烂实现,尽可能保持旧界面不变.

The reason for such design is that class is replacement for several legacy hand-made bicycle containers written by some prehistorics in early 90th. Idea is to replace old rotting implemenation with modern STL&Boost containers keeping old interface untouched as much as possible.

问题

当用户试图从某些专业调用不受支持的函数时,这种设计会导致不愉快的情况.它编译正常,但在链接阶段产生错误(符号未定义).不太友好的行为.

Such design leads to unpleasant situation when user tries to call unsupported function from some specialization. It compiles OK, but produces error on linking stage (symbol not defined). Not very user friendly behavior.

示例:

 SortedItemContainer sc;
 sc.IsEmpty(); // OK
 sc.Sort(); // OK

 IndexedItemContainer ic;
 ic.IsEmpty(); // OK
 ic.Sort(); // Compiles OK, but linking fails

当然,可以通过使用继承而不是特化来完全避免这种情况,但是我不喜欢生成大量具有 1-3 个函数的类.希望保留原创设计.

Of course, it could be completely avoided by using inheritance instead of specialization but I don't like to produce a lot of classes with 1-3 functions. Would like to keep original design.

有没有可能把它变成编译阶段错误而不是链接阶段一?我有一种感觉,可以以某种方式使用静态断言.

Is there possibility to turn it into compile stage error instead of link stage one? I have a feeling that static assert could be used somehow.

此代码的目标编译器是 VS2008,因此实际解决方案必须与 C++03 兼容,并且可以使用 MS 特定功能.但也欢迎可移植的 C++11 解决方案.

Target compiler for this code is VS2008, so practical solution must be C++03 compatible and could use MS specific features. But portable C++11 solutions also are welcome.

源代码:

// ItemContainer.h
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

template <class Impl> class ItemContainer
{
public:

   // Common functions supported by all specializations
   void Clear();
   bool IsEmpty() const;
   ...

   // Functions supported by sequenced specializations only
   ItemPtr operator[](size_t i_index) const; 
   ...

   // Functions supported by indexed specializations only
   ItemPtr operator[](const PrimaryKey& i_key) const;
   ...

   // Functions supported by sorted specializations only
   void Sort();
   ...

private:

   boost::scoped_ptr<Impl> m_data; ///< Internal container implementation

}; // class ItemContainer

// Forward declarations for pimpl classes, they are defined in ItemContainer.cpp
struct SequencedImpl;
struct IndexedImpl;
struct SortedImpl;

// Typedefs for specializations that are explicitly instantiated
typedef ItemContainer<SequencedImpl> SequencedItemContainer;
typedef ItemContainer<IndexedImpl> IndexedItemContainer;
typedef ItemContainer<SortedImpl> SortedItemContainer;

// ItemContainer.cpp
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

// Implementation classes definition, skipped as non-relevant
struct SequencedImpl { ... };
struct IndexedImpl { ... };
struct SortedImpl { ... };

// Explicit instantiation of members of SequencedItemContainer
template  void SequencedItemContainer::Clear(); // Common
template  bool SequencedItemContainer::IsEmpty() const; // Common
template  ItemPtr SequencedItemContainer::operator[](size_t i_index) const; // Specific

// Explicit instantiation of members of IndexedItemContainer
template  void IndexedItemContainer::Clear(); // Common
template  bool IndexedItemContainer::IsEmpty() const; // Common
template  ItemPtr IndexedItemContainer::operator[](const PrimaryKey& i_key) const; // Specific

// Explicit instantiation of members of SortedItemContainer
template  void SortedItemContainer::Clear(); // Common
template  bool SortedItemContainer::IsEmpty() const; // Common
template  void SortedItemContainer::Sort(); // Specific

// Common functions are implemented as main template members
template <class Impl> bool ItemContainer<Impl>::IsEmpty() const
{
   return m_data->empty(); // Just sample
}

// Specialized functions are implemented as specialized members (partial specialization)
template <> void SortedItemContaner::Sort()
{
   std::sort(m_data.begin(), m_data.end(), SortFunctor()); // Just sample
}

...
// etc

推荐答案

尽管建议使用 SFINAE 的答案很好,但我继续寻找符合我原始设计的解决方案.终于找到了.

Despite the good answers suggesting to use SFINAE, I continued to search solution that will meet my original design. And finally I found it.

关键思想是对特定的函数成员使用特殊化,而不是显式实例化.

The key idea is to use specialization for specific function members instead of explicit instantiation.

做了什么:

  1. 为主模板添加了特定功能的虚拟实现.仅包含静态断言的实现放置在头文件中,但未内联到类定义中.
  2. .cpp 文件中删除了特定函数显式实例化.
  3. 在头文件中添加了特定的函数专业化声明.
  1. Added specific functions dummy implementation for main template. Implementations containing static asserts only were placed in header file but not inlined into class definition.
  2. Specific functions explicit instantiations were removed from .cpp file.
  3. Specific functions specialization declarations were added to header file.

源代码:

// ItemContainer.h
//////////////////////////////////////////////////////////////////////////////
template <class Impl> class ItemContainer
{
public:

   // Common functions supported by all specializations
   void Clear();
   bool IsEmpty() const;
   ...

   // Functions supported by sorted specializations only
   void Sort();
   ...

private:

   boost::scoped_ptr<Impl> m_data; ///< Internal container implementation

}; // class ItemContainer

// Dummy implementation of specialized function for main template
template <class Impl> void ItemContainer<Impl>::Sort()
{
   // This function is unsupported in calling specialization
   BOOST_STATIC_ASSERT(false);
}

// Forward declarations for pimpl classes,
// they are defined in ItemContainer.cpp
struct SortedImpl;

// Typedefs for specializations that are explicitly instantiated
typedef ItemContainer<SortedImpl> SortedItemContainer;

// Forward declaration of specialized function member
template<> void CSortedOrderContainer::Sort();

// ItemContainer.cpp
//////////////////////////////////////////////////////////////////////////////

// Implementation classes definition, skipped as non-relevant
struct SortedImpl { ... };

// Explicit instantiation of common members of SortedItemContainer
template  void SortedItemContainer::Clear();
template  bool SortedItemContainer::IsEmpty() const;

// Common functions are implemented as main template members
template <class Impl> bool ItemContainer<Impl>::IsEmpty() const
{
   return m_data->empty(); // Just sample
}

// Specialized functions are implemented as specialized members
// (partial specialization)
template <> void SortedItemContaner::Sort()
{
   std::sort(m_data.begin(), m_data.end(), SortFunctor()); // Just sample
}

...
// etc

<小时>

这种方式至少适用于 VS2008.


This way it works at least for VS2008.

对于带有 C++11 的 GCC static_assert 使用需要一些技巧来启用惰性模板函数实例化 (编译样本):

For GCC with C++11 static_assert usage requires some trick to enable lazy template function instatiation (compiled sample):

template <class T> struct X
{
    void f();
};

template<class T> void X<T>::f()
{
   // Could not just use static_assert(false) - it will not compile.
   // sizeof(T) == 0 is calculated only on template instantiation and       
   // doesn't produce immediate compilation error
   static_assert(sizeof(T) == 0, "Not implemented");
}

template<> void X<int>::f()
{
  std::cout << "X<int>::f() called" << std::endl;
}

int main()
{
   X<int> a;
   a.f(); // Compiles OK

   X<double> b;
   b.f(); // Compilation error - Not implemented!
}

这篇关于非实例化模板成员的编译时错误而不是链接时错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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