为什么模板化类的模板化typef语法无效?

时间:2014-10-07 01:33:17

标签: c++ templates c++11 type-conversion variadic-templates

使用MSVC12时,我尝试typedef依赖类型名称时会出现奇怪的语法错误:

template <typename ... LeftT>
struct A{
    template <typename ...>
    struct B{};

    template <typename T, typename ... RightT>
    struct B <T, RightT ... >{
        typedef typename A<LeftT ..., T> nextA;
        typedef typename nextA::B<RightT ...> nextB; //error C2059: syntax error : '<'
    };
};

这就是它的一切,模板永远不会被实例化,除了内部。

我的问题:为什么我在这里遇到语法错误?这是无效的语法吗?

你质疑(可能):你为什么要这样做?

基本思想是通过类型转换进行转发。这可以通过专门化B来完成,以便T具有某种类型,然后可以转换。下面将详细介绍其工作原理,包括完整的代码段。

如果这个问题措辞严厉或难以理解,请帮助我改进。这是我一直在研究英语的最复杂的模板问题之一,不是我的母语。


为什么疯狂?

作为一个练习,我想编写一个模板构造,允许我调用printf以及需要char[]std::string s的类似函数。

为此,我必须遍历每一个参数,并专注于它可能是std::string的情况。为了做到这一点,我有一个结构fwd<typename func_t, typename ... LeftArgs>,它包含已处理的所有参数,并在结构one_arg<typename Arg, typename ... RightArgs>内,允许通过专用于空基数来处理当前参数案例和Argstd::string的案例。基本情况(没有参数,或者没有剩下要处理),如下处理:

template<typename ...>
struct one_arg
{
    //implementation of return_type will be shown below
    static return_type forward(func_t func, LeftArgs ... leftArgs){
        return func(leftArgs ...); //execute function
    }
};

基础案例的专业化是一个常见的情况,即参数除std::string以外的任何类型:

template<typename Arg, typename ... RightArgs>
struct one_arg < Arg, RightArgs ...>
{
    //move Arg to the processed arguments
    typedef typename fwd< func_t, LeftArgs..., Arg>::one_arg<RightArgs...> next_arg;

    static return_type forward(func_t func, LeftArgs ... leftArgs, Arg arg, RightArgs ... rightArgs){
        return next_arg::forward(func, leftArgs ..., arg, rightArgs ...);
    }
};

使用几乎相同的语法进一步专门研究这种常见情况,当前参数的确属于std::string类型:

template<typename ... RightArgs>
struct one_arg < std::string, RightArgs ...> 
{
    //again, move Arg to the processed arguments, but as a char[]
    typedef typename fwd< func_t, LeftArgs..., char[]>::one_arg<RightArgs...> next_arg;

    static return_type forward(func_t func, LeftArgs ... leftArgs, std::string arg, RightArgs ... rightArgs){
        //convert string to char[]
        return next_arg::forward(func, leftArgs ..., arg.c_str(), rightArgs ...);
    }
};

我希望结构很明显。如果没有,这是完整的片段,准备好了。

完整代码段:

#include <string>

using namespace std;

//get the return type of a function
template <typename T>
struct get_return_type;
template <typename R, typename ... A>
struct get_return_type<R(A...,...)>
{
    typedef R type;
};

template<typename func_t, typename ... LeftArgs>
struct fwd{
    typedef typename get_return_type<func_t> return_type;

    //empty base case
    template<typename ...>
    struct one_arg
    {
        static return_type forward(func_t func, LeftArgs ... leftArgs){
            return func(leftArgs ...);
        }
    };

    //normal forwarding
    template<typename Arg, typename ... RightArgs>
    struct one_arg < Arg, RightArgs ...>
    {
        typedef typename fwd< func_t, LeftArgs..., Arg>::one_arg<RightArgs...> next_arg;

        static get_return_type<func_t> forward(func_t func, LeftArgs ... leftArgs, Arg arg, RightArgs ... rightArgs){
            return next_arg::forward(func, leftArgs ..., arg, rightArgs ...);
        }
    };

    //specialisation for std::string
    template<typename ... RightArgs>
    struct one_arg < std::string, RightArgs ...> 
    {
        typedef typename fwd< func_t, LeftArgs..., char[]>::one_arg<RightArgs...> next_arg;

        static get_return_type<func_t> forward(func_t func, LeftArgs ... leftArgs, std::string arg, RightArgs ... rightArgs){
            return next_arg::forward(func, leftArgs ..., arg.c_str(), rightArgs ...);
        }
    };
};

template<typename func_t, typename ... Args>
typename fwd<func_t>::one_arg<Args ...>::return_type forward_stoc(func_t func, Args ... args){
    typedef typename fwd<func_t>::one_arg<Args ...> next_arg;
    return next_arg::forward(func, args...);
}

1 个答案:

答案 0 :(得分:2)

nextA是一个依赖范围,因此您需要指明B是模板的名称:

typedef typename nextA::template B<RightT ...> nextB;
                        ^^^^^^^^

请注意,typename的声明中不应该有nextA,因为没有涉及的依赖范围:

typedef A<LeftT ..., T> nextA;

有关血腥的详细信息,请参阅Where and why do I have to put the "template" and "typename" keywords?