【问题标题】:Variadic template recursive types passing可变参数模板递归类型传递
【发布时间】:2015-11-05 08:04:43
【问题描述】:

我为我的问题找到了几乎令人满意的解决方案here(第二个答案),但我不能在另一个编译单元中使用这种方式编写的代码,因为将代码放在头文件中会导致链接器抱怨多个函数定义并且只放置声明在头文件中导致链接器未定义引用问题。

这是我的代码:

template <typename... types>
void foo();

template<>
void foo<>() {
    return;
}

template<typename type, typename... types>
void foohelper() {
    foo<types...>();
}

template <typename... types>
void foo() {
    foohelper<types...>();
}

int main() {
    foo<int, int>();
}

这就是我想要实现的目标:

class A {
public:
    template<>
    void foo<>() {
        return;
    }

    template<typename parser, typename... parsers>
    void foohelper() {
        foo<parsers...>();
    }

    template <typename... parsers>
    void foo() {
        foohelper<parsers...>();
    }
};

int main() {
    A a;
    a.foo<int, int>();
}

但这会在编译过程中导致以下错误:

explicit specialization 'void A::foo(void)' is not a specialization of a function template

有什么简单的解决办法吗?

【问题讨论】:

  • 您是否尝试将通用(非专业)foo 声明移到专业声明之上,并将所有定义移到类定义之后?

标签: c++ templates recursion variadic


【解决方案1】:

不需要递归。这更简单:

#include <iostream>
#include <string>
#include <typeinfo>

class A {
public:

    template<typename parser>
    void foohelper() {
        std::cout << "handled a " << typeid(parser).name() << std::endl;
        // do work here
    }

    template <typename... parsers>
    void foo() {
        using expand = int[];
        (void) expand { 0, (foohelper<parsers>(), 0)... };
    }
};

int main() {
    A a;
    a.foo<int, int, double, std::string>();
}

样本输出:

handled a i
handled a i
handled a d
handled a NSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE

编辑:

针对不符合标准的 microsoft 编译器要求,这里有另一个不依赖 unsized 数组的版本:

#include <iostream>
#include <string>
#include <typeinfo>

class A {
public:

    template<typename parser>
    void foohelper() {
        std::cout << "handled a " << typeid(parser).name() << std::endl;
        // do work here
    }

    template <typename... parsers>
    void foo() {
        // c++ strictly does not allow 0-sized arrays.
        // so here we add a NOP just in case parsers is an empty type list
        using expand = int[1 + sizeof...(parsers)];
        (void) expand {
            (foohelper<void>(), 0),
            (foohelper<parsers>(), 0)...
        };
    }

};

// implement the NOP operation. Note specialisation is outside class definition.
template<> void
A::foohelper<void>() {}



int main() {
    A a;
    a.foo<int, int, double, std::string>();
    a.foo<>();
}

编辑 2:

带有前缀、后缀和解析器间调用的完整示例。编写了这么多代码后,您可能会开始想,“嘿!我可以在这里实现一门特定领域的语言!”,你是对的。

但是,比这更复杂的事情可能会让你永远憎恨你的同事,所以我会避免走这条路。

#include <iostream>
#include <string>
#include <typeinfo>

class A {
public:

    template<typename parser>
    void foohelper() {
        std::cout << "handled a " << typeid(parser).name();
        // do work here
    }

    void prepare()
    {
        std::cout << "starting parsers: ";
    }

    void separator()
    {
        std::cout << ", ";
    }

    void nothing()
    {

    }

    void done() {
        std::cout << " done!" << std::endl;
    }

    template <typename... parsers>
    void foo() {
        // c++ strictly does not allow 0-sized arrays.
        // so here we add a NOP just in case parsers is an empty type list
        bool between = false;
        using expand = int[2 + sizeof...(parsers)];
        (void) expand {
            (prepare(), 0),
            ((between ? separator() : nothing()), between = true, foohelper<parsers>(), 0)...,
            (done(), 0)
        };
    }

};


int main() {
    A a;
    a.foo<int, int, double, std::string>();
    a.foo<>();
}

样本输出:

starting parsers: handled a i, handled a i, handled a d, handled a NSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE done!
starting parsers:  done!

【讨论】:

  • 完美!它甚至可以使用 arm-none-eabi 编译器进行编译。
  • 我在使用 VisualStudio 2013(平台工具集 v120)编译时收到警告,当使用比 sn-p 更复杂的程序时会导致崩溃。警告:“警告 C4789:大小为 4 字节的缓冲区 '' 将被溢出;将从偏移量 4 开始写入 4 个字节” 警告与偏移量增加 4 个字节的模板参数一样多。
  • 秒,给我一点时间为你的编译器做一个解决方法。微软编译器总是有点微妙:)
  • @omicronns 完成。试试看。我已经使用 -Wpedantic 在 clang 上对其进行了测试。
  • 为什么需要foohelper&lt;void&gt;()0 还不够好吗?作为旁注,原始代码在 VC++ 2015 中运行良好。另一个旁注:foohelper&lt;void&gt; 定义为 inline 以避免问题中提到的 OP 的多重定义问题。
猜你喜欢
  • 2017-03-18
  • 2017-02-23
  • 1970-01-01
  • 2017-07-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-03-31
  • 2013-08-04
相关资源
最近更新 更多