这是我在重构一些代码时偶然发现的一个问题,我想知道是否有更好的方法来做到这一点:
#include <iostream>
template<typename T>
class Foo
{
public:
Foo()
{
init(x);
}
T x;
};
void init(int& i)
{
i = 42;
}
int main()
{
Foo<int> foo;
std::cout << foo.x << std::endl;
return 0;
}
不幸的是,这无法通过 GCC 或 Clang 编译。 Foo 的构造函数中调用的函数 init
未声明。在这个玩具示例中,这可以通过将函数本身移到模板之前来解决。但是,在更复杂的上下文中,这可能不起作用。最初,我打算使用 init
的重载来允许对模板中使用的类进行一些设置。
我假设在此上下文中 init
是一个非依赖名称 - 即使函数调用的参数依赖于模板参数(起初对我来说这很奇怪)。有没有办法让它也考虑在模板本身之后定义的函数定义?
我知道我可以使用模板特化(最初是在原始代码中,但我想用更简单的重载替换它):
template<typename>
struct Initializer;
template<>
struct Initializer<int>
{
static void init(int& i)
{
i = 42;
}
}
有没有一种方法可以使它与函数重载一起工作?我知道,boost::serialization
也依赖于自定义类型的函数重载,但我并没有真正找到他们在哪里以及如何实现它。
最佳答案
您可以通过调用模板函数对象来解决排序问题 - 与 boost::hash
查找 ADL 函数 hash_value(x)
的方式相同。
这是可行的,因为模板的扩展被推迟到第一次使用时:
#include <iostream>
namespace A {
struct XX {
friend void init(XX&);
};
}
namespace B {
struct YY {
friend void init(YY&);
};
}
/// default case - call init on T found by ADL
template<class T>
struct call_init
{
void operator()(T& o) const {
init(o);
}
};
template<typename T>
class Foo
{
public:
Foo()
{
auto initialiser = call_init<decltype(this->x)>();
initialiser(this->x);
}
T x;
};
void init(int& x) {
x = 2;
}
// special case, initialise an int
template<> struct call_init<int>
{
void operator()(int& x) const {
init(x);
}
};
int main()
{
Foo<int> foo;
Foo<A::XX> foox;
Foo<B::YY> fooy;
std::cout << foo.x << std::endl;
return 0;
}
关于C++ 模板和函数解析,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43753724/