变异模板 - 不完整类型

sma*_*llB 7 c++ compiler-errors variadic-templates c++11

有这个代码:

template<class ...Args>
struct Are_Same
{
    enum {value = Are_Same<Args...>::value};
};

template<class A,class... C>
struct Are_Same<A,C...>
{
    enum {value = Are_Same<A,C...>::value};//HERE is THE ERROREOUS LINE
};

template<class A,class B>
struct Are_Same<A,B>
{
    enum {value = std::is_same<A,B>::value};
};  
Run Code Online (Sandbox Code Playgroud)

我从gcc 4.6.1收到错误:

错误:嵌套名称说明符中使用的不完整类型'Are_Same'.

我认为,通过这样做,Are_Same<A,C...>::value我将调用递归调用,最后只会扩展到Are_Same<A,B>.显然事实并非如此.谁知道我在哪里弄错了?

Dav*_*eas 8

我认为模板的定义是错误的,在这两种情况下你都会触发精确的递归.我原以为编译器会在编译器内部出现一些stackoverflow但会产生不同的错误......

are_same可变参数模板的实现可以是:

template <class... Args>                    // base (optional to declare the template)
struct are_same;

template <class A, class B, class... Args>  // recursion
struct are_same<A,B,Args...> {
    static const bool value = is_same<A,B>::value && are_same<A,Args...>::value;
};

template <class A, class B>                 // stop condition
struct are_same<A,B> {
    static const bool value = is_same<A,B>::value;
};
Run Code Online (Sandbox Code Playgroud)

请注意,在该recursion步骤中,将从参数列表中删除一个参数,以便要解决的新问题是原始版本的缩减版本.这种类型的模板元编程与递归非常相关,并且适用相同的规则,以便能够使用递归来确保每个递归步骤使您更接近解决方案.在这种特殊情况下,给定N个可能相同类型的列表,每个步骤减少了查找N-1类型是否相同的问题.

您也可以使用停止条件(替换前一个)作为are_same问题的退化版本:

template <class A> 
struct are_same<A> {
   static const bool value = true;
};
Run Code Online (Sandbox Code Playgroud)

哪个是退化的,因为询问单个类型*is_same*是否真的没有意义,但对于不同的元编程任务,它可能是合适的.

一个不同的可能更有效的算法(我不确定编译器是否会在上面的递归步骤中避免模板的实例化)不依赖于is_same:

template <class... Args>
struct are_same;

template <class A, class... Args>
struct are_same<A,A,Args...> {              // recursion
    static const bool value = are_same<A,Args...>::value;
};

template <class A, class B, class... Args>
struct are_same<A,B,Args...> {              // cut, A and B are not the same
    static const bool value = false;
};

template <class A>
struct are_same<A> {                        // end of recursion
    static const bool value = true;
};
Run Code Online (Sandbox Code Playgroud)

在这种情况下,编译器会喜欢recursioncut每当两个类型相同的步骤,所以我们没有必要检查is_same内部.同时,如果编译器进入该cut步骤,我们不需要处理类型列表的其余部分,因为我们已经知道了答案.