对于我的一个项目,我需要使用可变参数模板。一切都运作良好,除了参数的解包。
这是电话
Shader<GL_VERTEX_SHADER> vert(vertexShaderSource);
Shader<GL_FRAGMENT_SHADER> frag(fragmentShaderSource);
Program prog(vert, frag);
导致问题的类
class Program
{
public:
template <class... Args>
Program(Args... args) :
program_(glCreateProgram())
{
auto shaders {{args...}};
std::for_each(shaders.begin(), shaders.end(), [this](auto s)
{
std::cout << "glAttachShader\n";
glAttachShader(program_, s.get_shader());
});
}
};
错误
fatal error: cannot deduce type for variable 'shaders' with type 'auto' from nested initializer list
auto shaders {{args...}};
我尝试了几件事,比如
auto shaders = {args...};
auto shaders = {{args...}};
auto shaders {args...};
但没有任何作用。
以下是Shader课程,以防万一
template <GLenum type>
class Shader
{
public:
Shader(std::string const &source)
{
char const *src = source.c_str();
shader_ = glCreateShader(type);
glShaderSource(shader_, 1, &src, NULL);
glCompileShader(shader_);
}
~Shader()
{
glDeleteShader(shader_);
}
inline GLuint get_shader()
{
return shader_;
}
private:
GLuint shader_;
};
谢谢!
答案 0 :(得分:6)
这不是关于可变参数模板解包的。问题是在声明着色器时,您需要告诉它的类型。它是矢量,数组,元组等吗? 由于您正在使用可变参数模板,因此我猜测着色器可以有不同的类型。然后你必须使用一个元组。
auto shaders = std::make_tuple(args...);
迭代元组并不像stl容器那样微不足道。这是一个使用递归的例子。
template <size_t i = 0,
class Fun,
class Tuple,
size_t N = std::tuple_size<typename std::decay<Tuple>::type>::value,
std::enable_if_t<i >= N>* = nullptr> // if i >= N
void tuple_for_each(Tuple&& t, Fun f) {} // end case
template <size_t i = 0,
class Fun,
class Tuple,
size_t N = std::tuple_size<typename std::decay<Tuple>::type>::value,
std::enable_if_t<i < N>* = nullptr> // if i < N
void tuple_for_each(Tuple&& t, Fun f) {
f(std::get<i>(std::forward<Tuple>(t))); // current iteration
tuple_for_each<i+1>(std::forward<Tuple>(t), std::move(f)); // call next
}
总的来说,它非常直观,我们从i = 0开始,调用f()然后使用递归来遍历每个i直到N. C ++ 14允许你避免使用std :: integer_sequence递归,你可以搜索
如果你想知道这个和&amp;&amp;胡说八道,我建议你阅读Universal References。简而言之,它允许您使用引用来防止复制参数,同时可以处理r值。我建议你为程序构造函数中的Args做同样的事情。
然后我们可以使用tuple_for_each来做
tuple_for_each(shaders, [this](auto s) {
std::cout << "glAttachShader\n";
glAttachShader(program_, s.get_shader());
});
答案 1 :(得分:3)
您可以使用数组来解包参数,而不是使用std::for_each
。
诀窍是使用list-initialization来解包参数,调用lambda,并使用,
运算符来删除值并使用0
代替。
struct Program {
template <class... Args>
Program(Args... args) : program_(glCreateProgram()) {
int unpack[] = {([this](auto& shader){
std::cout << "glAttachShader\n";
glAttachShader(program_, s.get_shader());
}(args), 0)..., 0};
// use this to silent the warning
static_cast<void>(unpack);
}
};
你可以通过发送lambda而不是硬编码来概括这个“包foreach”:
template<typename F, typename... Args>
void pack_foreach(F f, Args&&... args) {
int unpack[] = {(f(std::forward<Args>(args)), 0)..., 0};
static_cast<void>(unpack);
}
离开你的代码:
struct Program {
template <class... Args>
Program(Args... args) : program_(glCreateProgram()) {
pack_foreach([this](auto shader){
std::cout << "glAttachShader\n";
glAttachShader(program_, s.get_shader());
}, args...);
}
};
答案 2 :(得分:2)
另一种方式 - 将复杂的东西放入自己的私有方法中:
class Program
{
template<class ShaderTuple, std::size_t...Is>
void attach_shaders(const ShaderTuple& shaders, std::index_sequence<Is...>)
{
using expand = int[];
void(expand
{
0,
(
std::cout << "glAttachShader\n",
glAttachShader(program_, std::get<Is>(shaders).get_shader()),
0
)...
});
}
public:
template <class... Shaders>
Program(Shaders&&... args)
: program_(glCreateProgram())
{
attach_shaders(std::make_tuple(std::forward<Shaders>(args)...),
std::make_index_sequence<sizeof...(Shaders)>());
}
};
答案 3 :(得分:0)
基于环路的范围在这种情况下非常适合
另外,尝试在lambda中直接使用auto
来避免额外的副本
这是一个最小的工作示例:
#include<iostream>
class Program {
public:
template <class... Args>
Program(Args... args) {
for(auto &&arg: { args... }) {
std::cout << "do whatever you want" << std::endl;
}
}
};
int main() {
Program p{42, 0, 1};
}
以下是您的代码审核:
class Program {
public:
template <class... Args>
Program(Args... args) : program_(glCreateProgram()) {
for(auto &&s: { args... }) {
std::cout << "glAttachShader" << std::endl;
glAttachShader(program_, s.get_shader());
}
}
};