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在这里具有正确的行为,正如链接问题中所讨论的那样,特别是这个答案.
在思考了你的问题之后,我看不出有什么方法可以实现这一目标。您需要一种方法使实例化“导出”模板外部的某些内容,以便可以在不引用实例化的情况下访问它。带有 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)
由您决定是否可以接受此宏。