std::enable_if 与非类型模板参数

8

如何在std::enable_if中使用非类型模板参数比较?我无法再次解决这个问题。(之前我曾经解决过这个问题,但是我现在已经找不到代码了,也无法找到之前得到答案的帖子。)

非常感谢您对这个问题提供任何帮助。

template<int Width, int Height, typename T>
class Matrix{
    static
    typename std::enable_if<Width == Height, Matrix<Width, Height, T>>::type
    Identity(){
        Matrix ret;
        for (int y = 0; y < Width; y++){
            elements[y][y] = T(1);
        }
        return ret;
    }
}

编辑:根据评论中指出的缺失的括号进行更正。


3
我会建议使用static_assert来实现。它可以提供清晰的错误信息。 - chris
static_assert确实是正确的工具:std::enable_if用于SFINAE,而对于类模板的非模板成员,不存在SFINAE。 - Luc Danton
我考虑使用static_assert,最终可能会使用它。但是在此之前,我已经让它工作了,并且由于自动完成不会在第一时间列出非方阵矩阵的函数,因此具有优势。然而,目前主要是试图弄清楚我曾经如何仅使用std::enable_if来完成这项任务。 - LostOfThought
enable_if 行末少了一个 >。实际上(在修正后)您的代码可以工作,但它与 static_assert 没有区别(当调用非方阵矩阵类型时,会给出编译错误)。 它只比 static_assert 更晦涩难懂。 - alfC
@alfC:我和你一样被咬了。即使进行了更正,非方阵矩阵仍然无法实例化。 - syam
显示剩余2条评论
2个回答

6

这完全取决于您希望在无效代码上引发什么样的错误/故障。以下是一种可能性(抛开明显的 static_assert(Width==Height, "not square matrix");

(C++98风格)

#include<type_traits>
template<int Width, int Height, typename T>
class Matrix{
public:
    template<int WDummy = Width, int HDummy = Height>
    static typename std::enable_if<WDummy == HDummy, Matrix>::type
    Identity(){
        Matrix ret;
        for (int y = 0; y < Width; y++){
        // elements[y][y] = T(1);
        }
        return ret;
    }
};

int main(){
    Matrix<5,5,double> m55;
    Matrix<4,5,double> m45; // ok
    Matrix<5,5, double> id55 = Matrix<5,5, double>::Identity(); // ok
//  Matrix<4,5, double> id45 = Matrix<4,5, double>::Identity(); // compilation error! 
//     and nice error: "no matching function for call to ‘Matrix<4, 5, double>::Identity()"
}

编辑: 在C++11中,代码可以更加简洁明了(它在clang 3.2中可以工作,但在gcc 4.7.1中不行,所以我不确定它有多标准):

(C++11风格)

template<int Width, int Height, typename T>
class Matrix{
public:
    template<typename = typename std::enable_if<Width == Height>::type>
    static Matrix
    Identity(){
        Matrix ret;
        for(int y = 0; y < Width; y++){
            // ret.elements[y][y] = T(1);
        }
        return ret;
    }
};

编辑 2020: (C++14)

template<int Width, int Height, typename T>
class Matrix{
public:
    template<typename = std::enable_if_t<Width == Height>>
    static Matrix
    Identity()
    {
        Matrix ret;
        for(int y = 0; y < Width; y++){
        //  ret.elements[y][y] = T(1);
        }
        return ret;
    }
};

(C++20)https://godbolt.org/z/cs1MWj
template<int Width, int Height, typename T>
class Matrix{
public:
    static Matrix
    Identity()
        requires(Width == Height)
    {
        Matrix ret;
        for(int y = 0; y < Width; y++){
        //  ret.elements[y][y] = T(1);
        }
        return ret;
    }
};

刚好赶在我完成自己的答案之前。无论如何,感谢你的回答。已标记为接受。 - LostOfThought
1
在C++11中有一种更紧凑的替代方案:template<int WDummy = Width, class Dummy = typename std::enable_if<WDummy == Height, Matrix>::type> static Matrix Identity(){...} - alfC
“WDummy”是否必要?使用MSVC:CTP Nov'12和/W4编译器,没有它也可以正常编译。 - LostOfThought
@LostOfThought,您是指在原始回答中吗?您可以使用template<int HDummy = Height> static typename std::enable_if<Width == HDummy, Matrix<Width, Height, T> >::type Identity(){...}。在注释中我仍然需要有WDummy,但我可以摆脱Dummytemplate<int WDummy = Width, class = typename std::enable_if<WDummy == Height, Matrix>::type> static Matrix Identity(){...} - alfC
1
我认为在C++98中,函数模板不能有默认参数。 - Puppy
我认为第二个选项只适用于C++11。第一个选项可以通过使用boost::enable_if_c来适应C++98。 - alfC

1
我在这里找到了我的问题的答案:使用C++11 std::enable_if启用... 在我的解决方案中,SFINAE出现在我的模板化返回类型中,因此使函数模板本身有效。在此过程中,函数本身也变成了模板化。
template<int Width, int Height, typename T>
class Matrix{
    template<typename EnabledType = T>
        static
        typename Matrix<Width, Height,
            typename std::enable_if<Width == Height, EnabledType>::type>
        Identity(){
        Matrix ret;
        for (int y = 0; y < Width; y++){
            ret.elements[y][y] = T(1);
        }
        return ret;
    }
}

我在我的回答中添加了一个更紧凑的版本,但它在gcc中无法运行。顺便说一句,在Matrix之前的typename最多是多余的。 - alfC
你编辑的代码与我的新答案中的代码相同,在此重复没有意义。 - alfC
我现在明白了,这不是我的本意。我整晚都在摆弄这个代码,真的很无聊。不管怎样,感谢您的帮助。 - LostOfThought
无论如何,你真的在实现一个矩阵类吗?不是要打击你的积极性,但你看过uBlas和Eigen吗?http://eigen.tuxfamily.org/index.php?title=Main_Page(这个甚至有像你设计的那样的静态数组大小),但重新实现很酷,我相信它没有一个很酷的“Identity”静态函数。 - alfC
我有几个朋友想制作一个游戏,其中一个正在上大学,主要学习汇编语言课程,而我来自AS3,那里有很多手把手的指导。我们两个似乎都在用最困难的方式做事情。我正在从头开始编写所有内容,甚至是我的OpenGL加载器,这对我来说一直是一场艰苦的斗争,特别是阅读C++11中“正确”做事情的方式,并且让它足够强大以应对未来我想做的任何事情。(更不用说所有东西都跨平台,只使用系统API和OpenGL)简而言之:是的。 - LostOfThought
好的一面是,现在我只剩下矩阵/矩阵数学函数了,其他大部分都已经完成,包括投影等等。(是的,反过来,我知道。) - LostOfThought

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接