如果模板尚未使用某种类型实例化,是否可能触发编译器/链接器错误?

Xeo*_*Xeo 17 c++ templates casting compiler-errors c++11

后续问题是[是否转换为指向模板的指针实例化该模板?].

问题就像标题所说的那样,问题的其余部分是类模板的约束和用法示例,以及我尝试实现目标.

一个重要的约束:用户通过继承我的类模板来实例化模板(而不是通过显式实例化它,如下面的尝试).因此,对我来说很重要的是,如果可能的话,用户不需要做任何额外的工作.只是子类化它应该工作(子类实际上在一个字典中注册自己,而不是用户除了用CRTP子类化一个额外的类模板之外的任何事情,子类永远不会被创建它的用户直接使用).我愿意接受用户需要做额外工作的答案(如从额外的基础派生),如果真的没有别的办法.


一个代码片段,用于解释如何使用类模板:

// the class template in question
template<class Resource>
struct loader
{
  typedef Resource res_type;
  virtual res_type load(std::string const& path) const = 0;
  virtual void unload(res_type const& res) const = 0;
};

template<class Resource, class Derived>
struct implement_loader
  : loader<Resource>
  , auto_register_in_dict<Derived>
{
};

template<class Resource>
Resource load(std::string const& path){
  // error should be triggered here
  check_loader_instantiated_with<Resource>();

  // search through resource cache
  // ...

  // if not yet loaded, load from disk
  // loader_dict is a mapping from strings (the file extension) to loader pointers
  auto loader_dict = get_all_loaders_for<Resource>();
  auto loader_it = loader_dict.find(get_extension(path))
  if(loader_it != loader_dict.end())
    return (*loader_it)->load(path);
  // if not found, throw some exception saying that
  // no loader for that specific file extension was found
}

// the above code comes from my library, the code below is from the user

struct some_loader
  : the_lib::implement_loader<my_fancy_struct, some_loader>
{
  // to be called during registration of the loader
  static std::string extension(){ return "mfs"; }
  // override the functions and load the resource
};
Run Code Online (Sandbox Code Playgroud)

现在以表格形式:

  • 用户the_lib::load<my_fancy_struct>使用资源路径调用
  • 在内部the_lib::load<my_fancy_struct>,如果路径标识的资源尚未缓存,我从磁盘加载它
  • loader在这种情况下使用的特定是在启动时创建并保存在字典中
  • 每种资源类型都有一个字典,它们映射[文件扩展名 - > loader指针]
  • 如果字典为空,则用户也是
    • 没有为该特定扩展创建一个加载器或
    • 没有为该特定资源创建加载器
  • 我只希望第一种情况让我抛出一个运行时异常
  • 第二种情况应该在编译/链接时检测,因为它涉及模板

理由:我非常支持早期错误,如果可能的话,我希望在运行之前检测尽可能多的错误,即在编译和链接时.由于检查该资源的加载器是否只存在模板,我希望可以这样做.


我尝试的目标:在调用时触发链接器错误check_error<char>.

// invoke with -std=c++0x on Clang and GCC, MSVC10+ already does this implicitly
#include <type_traits>

// the second parameter is for overload resolution in the first test
// literal '0' converts to as well to 'void*' as to 'foo<T>*'
// but it converts better to 'int' than to 'long'
template<class T>
void check_error(void*, long = 0);

template<class T>
struct foo{
  template<class U>
  friend typename std::enable_if<
    std::is_same<T,U>::value
  >::type check_error(foo<T>*, int = 0){}
};

template struct foo<int>;

void test();

int main(){ test(); }
Run Code Online (Sandbox Code Playgroud)

鉴于上述代码,以下test定义确实实现了MSVC,GCC 4.4.5和GCC 4.5.1的目标:

void test(){
  check_error<int>(0, 0); // no linker error
  check_error<char>(0, 0); // linker error for this call
}
Run Code Online (Sandbox Code Playgroud)

但是,它不应该这样做,因为传递空指针不会触发ADL.为什么需要ADL?因为标准如此说:

§7.3.1.2 [namespace.memdef] p3

[...]如果friend非本地类中的声明首先声明一个类或函数,那么友元类或函数是最内层封闭命名空间的成员.在该命名空间作用域中提供匹配的声明(在类定义授予友谊之前或之后)之前,通过非限定查找或限定查找找不到好友的名称.[...]

通过强制转换触发ADL,如下面的定义test,实现了Clang 3.1和GCC 4.4.5的目标,但GCC 4.5.1已经链接正常,MSVC10也是如此:

void test(){
  check_error<int>((foo<int>*)0);
  check_error<char>((foo<char>*)0);
}
Run Code Online (Sandbox Code Playgroud)

遗憾的是,GCC 4.5.1和MSVC10在这里具有正确的行为,正如链接问题中所讨论的那样,特别是这个答案.

Luc*_*lle 2

在思考了你的问题之后,我看不出有什么方法可以实现这一目标。您需要一种方法使实例化“导出”模板外部的某些内容,以便可以在不引用实例化的情况下访问它。带有 ADL 的函数friend是一个好主意,但不幸的是,事实表明,为了使 ADL 工作,必须实例化模板。我试图找到另一种从模板中“导出”某些内容的方法,但未能找到。

解决问题的通常方法是让用户专门化一个特征类:

template < typename Resource >
struct has_loader : boost::mpl::false_ {};

template <>
struct has_loader< my_fancy_struct > : boost::mpl::true_ {};
Run Code Online (Sandbox Code Playgroud)

要向用户隐藏这一点,您可以提供一个宏:

#define LOADER( loaderName, resource ) \
template <> struct has_loader< resource > : boost::mpl::true_ {}; \
class loaderName \
  : the_lib::loader< resource > \
  , the_lib::auto_register_in_dict< loaderName >

LOADER( some_loader, my_fancy_struct )
{
  public:
    my_fancy_struct load( std::string const & path );
};
Run Code Online (Sandbox Code Playgroud)

由您决定是否可以接受此宏。