重载可变参数模板方法
Overloading variadic templated methods
我的编译器很难理解这段代码,我花了好几个小时才找到问题所在。
#include <utility>
#include <string>
template<typename Derived>
struct AssetLoader {
template<typename... Args>
void doLoad(Args&& ... args) const {
static_cast<const Derived *>(this)->load(std::forward<Args>(args)...);
}
};
struct TextureLoader : public AssetLoader<TextureLoader> {
void load(const std::string &path) const {
// some code
}
};
struct SomeOtherLoader : public AssetLoader<SomeOtherLoader> {
void load(const std::string &path) const {
// some code
}
};
template<typename DefaultLoader>
class Resources {
AssetLoader<DefaultLoader> m_defaultLoader;
public:
Resources(AssetLoader<DefaultLoader> defaultLoader):
m_defaultLoader(std::move(defaultLoader)) {}
template<typename... Args>
void load(Args&& ... args) {
load(m_defaultLoader, std::forward<Args>(args)...);
}
template<typename Loader, typename... Args>
void load(const AssetLoader<Loader>& loader, Args&& ... args) {
loader.doLoad(std::forward<Args>(args)...);
}
};
int main() {
Resources<TextureLoader> resources(TextureLoader{});
resources.load("image.png");
resources.load(SomeOtherLoader{}, "example.jpg");
return 0;
}
我遇到了这个错误:
fatal error: template instantiation depth exceeds maximum of 900 (use -ftemplate-depth= to increase the maximum)
return load(m_defaultLoader, std::forward<Args>(args)...);
~~~~~~~~~~~~~~~~~~^~~~~~
我的实际代码要复杂得多,但我将其缩减为这个,但我得到了同样的错误。
如果我评论第一个重载,它运行良好,但我无法在不传递加载程序的情况下调用 load() 方法。我想要默认加载器的重载,所以我可以做 resources.load("image.png");
我用的是mingw64 8.1
有什么想法吗?
编译器准确地告诉您问题出在哪里 -- 这里是无限递归:
template<typename... Args>
void load(Args&& ... args) {
load(m_defaultLoader, std::forward<Args>(args)...);
}
这个函数正在无限地调用自己。永远不会选择另一个重载,因为 Args&&
合成了比 AssetLoader<Loader> const&
更好的匹配(具体来说,TextureLoader const&
)。
给另一个重载一个不同的名称以消除歧义...
template<typename... Args>
void load(Args&& ... args) {
load2(m_defaultLoader, std::forward<Args>(args)...);
}
template<typename Loader, typename... Args>
void load2(const AssetLoader<Loader>& loader, Args&& ... args) {
loader.doLoad(std::forward<Args>(args)...);
}
经过挖掘,我终于找到了解决方案。基本上我必须检查 Args...
的第一种类型本身不是加载程序,因此编译器将被迫选择第二个重载。
这是代码
#include <utility>
#include <string>
template<typename T1, typename...>
struct first {
typedef T1 type;
};
template<typename... T>
using first_t = typename first<T...>::type;
template<typename Derived>
struct AssetLoader {
template<typename... Args>
void doLoad(Args&& ... args) const {
static_cast<const Derived *>(this)->load(std::forward<Args>(args)...);
}
};
struct TextureLoader : public AssetLoader<TextureLoader> {
void load(const std::string& path) const {
// some code
}
};
struct SomeOtherLoader : public AssetLoader<SomeOtherLoader> {
void load(const std::string& path) const {
// some code
}
};
template<typename DefaultLoader,
std::enable_if_t<
std::is_base_of_v<
AssetLoader<DefaultLoader>,
DefaultLoader
>,
int
> = 0
>
class Resources {
DefaultLoader m_defaultLoader;
public:
Resources(DefaultLoader defaultLoader):
m_defaultLoader(std::move(defaultLoader)) {}
template<
typename... Args,
std::enable_if_t<
sizeof...(Args) == 0 ||
!std::is_base_of_v<
AssetLoader<std::decay_t<first_t<Args...>>>,
std::decay_t<first_t<Args...>>
>,
int
> = 0
>
void load(Args&& ... args) {
load(m_defaultLoader, std::forward<Args>(args)...);
}
template<typename Loader, typename... Args>
void load(const AssetLoader<Loader>& loader, Args&& ... args) {
loader.doLoad(std::forward<Args>(args)...);
}
};
int main() {
Resources<TextureLoader> resources(TextureLoader{});
resources.load("image.png");
resources.load(SomeOtherLoader{}, "example.jpg");
return 0;
}
我的编译器很难理解这段代码,我花了好几个小时才找到问题所在。
#include <utility>
#include <string>
template<typename Derived>
struct AssetLoader {
template<typename... Args>
void doLoad(Args&& ... args) const {
static_cast<const Derived *>(this)->load(std::forward<Args>(args)...);
}
};
struct TextureLoader : public AssetLoader<TextureLoader> {
void load(const std::string &path) const {
// some code
}
};
struct SomeOtherLoader : public AssetLoader<SomeOtherLoader> {
void load(const std::string &path) const {
// some code
}
};
template<typename DefaultLoader>
class Resources {
AssetLoader<DefaultLoader> m_defaultLoader;
public:
Resources(AssetLoader<DefaultLoader> defaultLoader):
m_defaultLoader(std::move(defaultLoader)) {}
template<typename... Args>
void load(Args&& ... args) {
load(m_defaultLoader, std::forward<Args>(args)...);
}
template<typename Loader, typename... Args>
void load(const AssetLoader<Loader>& loader, Args&& ... args) {
loader.doLoad(std::forward<Args>(args)...);
}
};
int main() {
Resources<TextureLoader> resources(TextureLoader{});
resources.load("image.png");
resources.load(SomeOtherLoader{}, "example.jpg");
return 0;
}
我遇到了这个错误:
fatal error: template instantiation depth exceeds maximum of 900 (use -ftemplate-depth= to increase the maximum)
return load(m_defaultLoader, std::forward<Args>(args)...);
~~~~~~~~~~~~~~~~~~^~~~~~
我的实际代码要复杂得多,但我将其缩减为这个,但我得到了同样的错误。
如果我评论第一个重载,它运行良好,但我无法在不传递加载程序的情况下调用 load() 方法。我想要默认加载器的重载,所以我可以做 resources.load("image.png");
我用的是mingw64 8.1
有什么想法吗?
编译器准确地告诉您问题出在哪里 -- 这里是无限递归:
template<typename... Args>
void load(Args&& ... args) {
load(m_defaultLoader, std::forward<Args>(args)...);
}
这个函数正在无限地调用自己。永远不会选择另一个重载,因为 Args&&
合成了比 AssetLoader<Loader> const&
更好的匹配(具体来说,TextureLoader const&
)。
给另一个重载一个不同的名称以消除歧义...
template<typename... Args>
void load(Args&& ... args) {
load2(m_defaultLoader, std::forward<Args>(args)...);
}
template<typename Loader, typename... Args>
void load2(const AssetLoader<Loader>& loader, Args&& ... args) {
loader.doLoad(std::forward<Args>(args)...);
}
经过挖掘,我终于找到了解决方案。基本上我必须检查 Args...
的第一种类型本身不是加载程序,因此编译器将被迫选择第二个重载。
这是代码
#include <utility>
#include <string>
template<typename T1, typename...>
struct first {
typedef T1 type;
};
template<typename... T>
using first_t = typename first<T...>::type;
template<typename Derived>
struct AssetLoader {
template<typename... Args>
void doLoad(Args&& ... args) const {
static_cast<const Derived *>(this)->load(std::forward<Args>(args)...);
}
};
struct TextureLoader : public AssetLoader<TextureLoader> {
void load(const std::string& path) const {
// some code
}
};
struct SomeOtherLoader : public AssetLoader<SomeOtherLoader> {
void load(const std::string& path) const {
// some code
}
};
template<typename DefaultLoader,
std::enable_if_t<
std::is_base_of_v<
AssetLoader<DefaultLoader>,
DefaultLoader
>,
int
> = 0
>
class Resources {
DefaultLoader m_defaultLoader;
public:
Resources(DefaultLoader defaultLoader):
m_defaultLoader(std::move(defaultLoader)) {}
template<
typename... Args,
std::enable_if_t<
sizeof...(Args) == 0 ||
!std::is_base_of_v<
AssetLoader<std::decay_t<first_t<Args...>>>,
std::decay_t<first_t<Args...>>
>,
int
> = 0
>
void load(Args&& ... args) {
load(m_defaultLoader, std::forward<Args>(args)...);
}
template<typename Loader, typename... Args>
void load(const AssetLoader<Loader>& loader, Args&& ... args) {
loader.doLoad(std::forward<Args>(args)...);
}
};
int main() {
Resources<TextureLoader> resources(TextureLoader{});
resources.load("image.png");
resources.load(SomeOtherLoader{}, "example.jpg");
return 0;
}