C++模板类内部非模板(但特定于类型)函数的使用



我得到的情况是,我得到了一个在很多项目中使用的通用库。 这个库只包含一个非常简单的模板类(在 template_class.h 内部)。然后,模板类将在多个可执行文件中使用(由 main.cpp 表示)。 main.cpp 还可以访问 autogenerated .h 中的一些自动生成的函数。 顾名思义,autogeneration.h 中的函数是自动生成的,无法修改。 这种"scaleFct"实现应该在模板函数中使用。 我的目的是使template_class独立于具体用法。

现在的问题是如何使自动生成的函数可在模板中使用。前向声明可能是一种选择吗?还是需要将函数指针作为模板参数传递?

这里有一个最小(不工作)的例子: (据我所知,这个示例甚至可以在 VC++ 中使用 - 但是现在我只能访问 gcc 7,它给了我一条错误消息:"'scaleFct' 没有在这个范围内声明,并且在实例化点通过依赖于参数的查找找不到声明">

提前非常感谢

// template_class.h
#pragma once
template<typename T>
class Aggregator
{
T sum_ = 0;
public:
T aggregate(T value){
sum_ += scaleFct(value);
return sum_;
}
};
// main.cpp
#include <iostream>
#include "template_class.h"
#include "autogenerated.h"
int main() {
Aggregator<float> aggregator;
std::cout << "current sum: " << aggregator.aggregate(1) << std::endl;
std::cout << "current sum: " << aggregator.aggregate(1) << std::endl;
return 0;
}
// autogenerated.h
#pragma once
float scaleFct(float val) {return val*2;}
double scaleFct(double val) {return val*2;}

当您在模板类中调用函数时,查找大致如下:

  1. 我可以在当前班级(不包括家长)中看到它吗?
  2. 我可以在类型不依赖于我的模板参数的父项中看到它吗?
  3. 我可以在声明模板的位置找到它吗?

所有这些都发生在替换模板参数之前。 这是在声明点完成的,而不是实例化。

传入类型后,将完成另一个查找:

  1. 我可以通过对函数的模板参数相关参数进行参数相关查找来找到它吗?

这是在实例化点完成的。 请注意,如果两个不同的实际实例化点(我认为包括发生实例化的翻译单元的末尾)导致不同的查找结果,则您的程序格式不正确,不需要诊断。

在早期版本的 MSVC 中,1 2 和 3 被延迟到实例化点,并且它还研究了其类型依赖于模板参数的基类。 所以你的代码有效。

每个现代编译器基本上都使用上述步骤(我可能犯了一些小错误,如果您需要确切地知道发生了什么,请进一步研究它;但上面应该为您提供要点)。

您将在声明点看到,scaleFct不可见,因此找不到。 并且参数相关查找不会发生在基元类型上,因此步骤 4 也找不到它。

解决问题的最简单/最佳方法是#include "autogenerated.h"template_class.h

另一个不太好的选择是在#include "template_class.h"之前#include "autogenerated.h"。 糟糕透了;您通常不希望头文件仅在C++中以特定顺序包含时才起作用,这会使您的代码库非常脆弱。

另一种选择是将template_class.h包装在包含自动生成的模板类标头的标头中。

另一种选择是将scaleFct的前向声明添加到template_class.h,可能将它们放入forward_declare_autogenerated.h头文件中。

另一种选择是 在Aggregator中添加蹦床 .

template<typename T, class Trampoline>
class Aggregator
{
T sum_ = 0;
public:
T aggregate(T value){
sum_ += Trampoline::scaleFct(value);
return sum_;
};
};

然后编写一个包含static成员的类Trampoline,将scaleFct引导到正确的操作。

另一个选项是特征类。

template<class T>
struct Traits {
static T scaleFct(T in){ return ::scaleFct(in); }
};
template<typename T, typename Trampoline=Traits<T>>
class Aggregator
{
T sum_ = 0;
public:
T aggregate(T value){
sum_ += Trampoline::scaleFct(value);
return sum_;
};
};

您专门针对每个T,并将调用注入到自动生成的代码中。 您也可以在此处传递明确的蹦床。

另一种方法是标记调度。

namespace adl {
template<class T>
struct tag_t {};
template<class T>
constexpr tag_t<T> tag{};
}
template<typename T>
class Aggregator
{
T sum_ = 0;
public:
T aggregate(T value){
sum_ += scaleFct(adl::tag<T>, value);
return sum_;
};
};

然后添加

namespace adl {
inline float scaleFct(tag_t<float>, float f) { return ::scaleFct(f); }
}

在做之前Aggregator<float>将通过ADL找到该功能。

最新更新