gpt4 book ai didi

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

转载 作者:IT老高 更新时间:2023-10-28 22:15:13 25 4
gpt4 key购买 nike

[Does casting to a pointer to a template instantiate that template?] 提出后续问题.

问题正如标题所说,剩下的问题是类模板的约束和使用示例,以及我为实现目标所做的尝试。

一个重要的约束:用户通过子类化我的类模板来实例化模板(而不是像下面我的尝试那样通过显式实例化它)。因此,对我来说重要的是,如果可能的话,用户不需要做任何额外的工作。只是子类化,它应该可以工作(子类实际上已经在字典中注册了自己,用户除了使用 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
};

现在是表格形式:

  • 用户调用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(); }

给定上面的代码,下面的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
}

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

§7.3.1.2 [namespace.memdef] p3

[...] If a friend declaration in a nonlocal class first declares a class or function the friend class or function is a member of the innermost enclosing namespace. The name of the friend is not found by unqualified lookup or by qualified lookup until a matching declaration is provided in that namespace scope (either before or after the class definition granting friendship). [...]

通过强制转换触发 ADL,如以下 test 的定义, 在 Clang 3.1 和 GCC 4.4.5 上实现了目标,但是 GCC 4.5.1 already links fine ,和 MSVC10 一样:

void test(){
check_error<int>((foo<int>*)0);
check_error<char>((foo<char>*)0);
}

遗憾的是,GCC 4.5.1 和 MSVC10 在这里有正确的行为,如链接问题中所述,特别是 this answer .

最佳答案

只要模板函数被引用并且模板的完整规范可用,编译器就会将其实例化。如果没有可用的,编译器不会,并希望其他一些翻译单元将其实例化。例如,基类的默认构造函数也是如此。

文件头.h:

template<class T>
class Base
{
public:
Base();
};

#ifndef OMIT_CONSTR
template<class T>
Base<T>::Base() { }
#endif

文件client.cc:

#include "header.h"

class MyClass : public Base<int>
{
};


int main()
{
MyClass a;
Base<double> b;
}

文件检查.cc:

#define OMIT_CONSTR
#include "header.h"

void checks()
{
Base<int> a;
Base<float> b;
}

然后:

 $ g++ client.cc check.cc
/tmp/cc4X95rY.o: In function `checks()':
check.cc:(.text+0x1c): undefined reference to `Base<float>::Base()'
collect2: ld returned 1 exit status

编辑:(试图将其应用于具体示例)

我将此文件称为“loader.h”:

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;

loader();
};

template<class Resource>
class check_loader_instantiated_with : public loader<Resource> {
virtual Resource load(std::string const& path) const { throw 42; }
virtual void unload(Resource const& res) const { }
};

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

还有另一个文件,“loader_impl.h”:

#include "loader.h"
template<class Resource>
loader<Resource>::loader() { }

据我所知,此解决方案有一个弱点。每个编译单元都可以选择仅包含 loader.h 或 loader_impl.h。您只能在包含 loader_impl 的编译单元中定义加载器,并且在这些编译单元中,所有加载器的错误检查都被禁用。

关于c++ - 如果没有使用某种类型实例化模板,是否可能触发编译器/链接器错误?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8393371/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com