如何包装可变参数模板函数?

时间:2018-10-14 03:10:52

标签: c++ g++ c++17

比方说,我有类似的东西

#include <iostream>

template <class Arg>
void helper(Arg&& arg) {
    // do something with the type and value
}

void vt(){}

template <class Arg, class... Args>
void vt(Arg&& arg, Args&&... rest) {
    helper(arg);
    vt(rest...);
}

int main() {
    vt(1, 1.1, "abc");
}

现在,我的问题是,如何在cpp文件中包装vt()之类的可变参数模板函数。基本上,我想对我的库中的客户端隐藏实现。

我考虑过使用va_list,但是它很慢,更重要的是,为了遍历列表,我需要提前知道类型,这是不可能的,因为我不知道调用者可能会做什么传递(除了变量参数必须是原始C类型)。

如果解决方案需要GCC专用的东西,我也可以。

如果解决方案必须删除可变参数模板,则可以解决,只要解决方案可以迭代可变参数,获取每个参数的类型(helper()需要信息)并获取每个参数的值即可。

谢谢!

2 个答案:

答案 0 :(得分:1)

有一个解决方案,但是它涉及typeinfo,可能会对性能产生影响。

#include <iostream>
#include <typeinfo>
#include <cstdarg>
using namespace std;

// This is the function you could puts its implementation in a separate file
// Use only its prototype in the header
void funcWithValist(int count, ...) {
    va_list a;
    va_start(a, count);
    for(int i=0;i<count;i++) {
        // Here is an example to extract the type info (you can use anything
        // instead of name() )
        cout << va_arg(a, std::type_info *)->name() << "\n";
    }
}

// The C++ wrapper function, this is to be put in the header file
template<typename... T>
void funcWithArgs(const T&... t) {
    funcWithValist(sizeof...(t), (&typeid(t))..., t...);
    // Expanded into: the number of args, &typeid(arg1), &typeid(arg2), ...
    //                arg1, arg2, ... 
}

int main() {
    // Example of the call
    funcWithArgs(4, std::string("Aaa"));
    return 0;
}

答案 1 :(得分:1)

问题是模板必须在标头中是有充分理由的。换句话说,并不是某些神秘的语言规则如此决定。

这样做的原因是模板功能是不是功能。它们是模板,使您能够生成函数。为此,用户代码必须能够看到整个模板定义。

请注意,如果您不需要模板使用每种可能的类型和任何可能的工具,您都可以这样做,但我建议这样做。

    #include<algorithm>
    #include<vector>
    #include<iostream>
    #include<variant>
    using my_v=std::variant<std::monostate, int, double, const char*>;
    void func(const my_v& a1= std::monostate{}, const my_v& a2 = std::monostate{},
              const my_v& a3 = std::monostate{} );
    struct visitor{
        void operator()(const my_v& v) const{
            if (std::holds_alternative<int>(v)){
                std::cout << "got int: " << std::get<int>(v) << std::endl;
            }
            if (std::holds_alternative<const char*>(v)){
                std::cout << "got string: " << std::get<const char*>(v) << std::endl;
            }
            if (std::holds_alternative<double>(v)){
                std::cout << "got double: " << std::get<double>(v) << std::endl;
            }
            if (std::holds_alternative<std::monostate>(v)){
                // empty function argument
            }

        }
    };

    void func(const my_v& a1, const my_v& a2, const my_v& a3){
        std::visit(visitor{}, a1); 
        std::visit(visitor{}, a2); 
        std::visit(visitor{}, a3); 
    }

    int main()
    {
        func(double{42}, "hello");
    }