使用带有模板参数的成员函数指针

Posted

技术标签:

【中文标题】使用带有模板参数的成员函数指针【英文标题】:Using a member function pointer with a template argument 【发布时间】:2020-03-23 21:45:07 【问题描述】:

我目前正在尝试制作一个通用函数,用于在另一个对象的类(特别是 Shader 类)中设置各种值。以下是我尝试设置的一些示例:

void setBool(const std::string& name, bool value) const 
    glUniform1i(glGetUniformLocation(shaderProgram, name.c_str()), (int)value);


void setInt(const std::string& name, int value) const 
    glUniform1i(glGetUniformLocation(shaderProgram, name.c_str()), value);


void setFloat(const std::string& name, float value) const 
    glUniform1f(glGetUniformLocation(shaderProgram, name.c_str()), value);


void setVec2(const std::string& name, const glm::vec2& value) const 
    glUniform2fv(glGetUniformLocation(shaderProgram, name.c_str()), 1, &value[0]);


void setVec3(const std::string& name, const glm::vec3& value) const 
    glUniform3fv(glGetUniformLocation(shaderProgram, name.c_str()), 1, &value[0]);


void setVec4(const std::string& name, const glm::vec4& value) const 
    glUniform4fv(glGetUniformLocation(shaderProgram, name.c_str()), 1, &value[0]);


void setMat2(const std::string& name, const glm::mat2& mat) const 
    glUniformMatrix2fv(glGetUniformLocation(shaderProgram, name.c_str()), 1, GL_FALSE, &mat[0][0]);


void setMat3(const std::string& name, const glm::mat3& mat) const 
    glUniformMatrix3fv(glGetUniformLocation(shaderProgram, name.c_str()), 1, GL_FALSE, &mat[0][0]);


void setMat4(const std::string& name, const glm::mat4& mat) const 
    glUniformMatrix4fv(glGetUniformLocation(shaderProgram, name.c_str()), 1, GL_FALSE, &mat[0][0]);


这是我正在尝试使用的功能:

template <class T>
void setUniforms(void(Shader::*fp)(const std::string&, const T&), const std::string& name, T value) 
    for (auto iter = shaderMap.cbegin(); iter != shaderMap.cend(); ++iter) 
        (iter->second->*fp)(name, T(value));
    
;

这是我的函数调用:

manager.setUniforms<const glm::mat4&>(&Shader::setMat4, "view", Camera::getLookMat());

其中Camera::getLookMat() 返回glm::mat4。但是,当我尝试编译时,出现此错误:

Error   C2664   'void ShaderManager::setUniforms<const glm::mat4&>(void (__thiscall Shader::* )(const std::string &,T),const std::string &,T)': cannot convert argument 1 from 'void (__thiscall Shader::* )(const std::string &,const glm::mat4 &) const' to 'void (__thiscall Shader::* )(const std::string &,T)' C2MEngine   D:\Programming\C++\C2MEngine\src\testing\Main.cpp   85  

有什么方法可以重新配置函数以使用列出的函数?这篇文章中的所有函数都是成员函数,第二个代码块是与第一个代码块不同的类的一部分。我不确定是成员函数指针还是模板有问题。

【问题讨论】:

【参考方案1】:

重新格式化该错误消息:

Error   C2664   
'void ShaderManager::setUniforms<const glm::mat4&>(
    void (__thiscall Shader::* )(const std::string &,T),
    const std::string &,T)':
cannot convert argument 1 from
    'void (__thiscall Shader::* )(const std::string &,const glm::mat4 &) const'
to
    'void (__thiscall Shader::* )(const std::string &,T)'
C2MEngine   D:\Programming\C++\C2MEngine\src\testing\Main.cpp   85  

类型的一个区别当然是通用模板参数T 代替了特定类型const glm::mat4&amp;。您将模板参数指定为const glm::mat4&amp;,这样应该没问题。另一个区别是导致问题的原因:注意第一个函数指针类型末尾的 const

成员函数类型上的const 是函数类型的一部分,对于指向这些类型的指针没有标准转换,以确保不会意外绕过 const 正确性。

所以在函数指针类型中添加最后的const

template <class T>
void setUniforms(void(Shader::*fp)(const std::string&, const T&) const,
                 const std::string& name,
                 T value);

(根据您问题中的声明修改,虽然错误消息暗示函数指针的值参数的类型为T,而不是const T&amp;。)

如果你愿意,你也可以使用别名模板来减少冗长,fp 函数参数的名称更明显:

template <class T> using ShaderSetFuncPtr =
    void (Shader::*)(const std::string&, const T&); // or ,T ?
template <class T>
void setUniforms(ShaderSetFuncPtr<T> fp, const std::string& name, T value);

【讨论】:

【参考方案2】:

您的成员函数指针中缺少const 限定符:

template <class T>
void setUniforms(void(Shader::*fp)(const std::string&, const T&) const, const std::string& name, T value)
//                                              added const here ~~~~^

虽然您所有的二传手(令人惊讶!)const-qualified。

【讨论】:

以上是关于使用带有模板参数的成员函数指针的主要内容,如果未能解决你的问题,请参考以下文章

泛型成员函数指针作为模板参数

带有接受派生类参数的基类参数的成员函数指针

如何创建带有参数的成员函数指针数组?

GCC C++14/17 成员函数指针模板参数的区别

提升 C++。将带有签名指针的成员函数指针传递给函数

std::async 与共享指针模板化成员函数