可变参数模板方法

问题描述 投票:0回答:3

我将创建一个采用可变参数(在模板中)的类方法,然后使用它们来获取(例如)所传递参数的名称。

我尝试实现(但不起作用):

#include <iostream>


struct St1 {};
struct St2 {};
struct St3 {};


class Test {
public:
    template<typename T>
    void method()
    {
        std::cout << typeid(T).name() << std::endl; // for example
    }

    template<typename T, typename ...Targs>
    void method()
    {
        method<T>();
        method<Targs...>();
    }
};

int main(void) {
    Test a;

    a.method<St1, St2, St3>();
    a.method<St1>();
}

而且当我要编译时,出现这些错误,'模棱两可':

testSpeed.cpp:29:19: error: call of overloaded ‘method()’ is ambiguous
     a.method<St1>();
                   ^
testSpeed.cpp:12:10: note: candidate: void Test::method() [with T = St1]
     void method()
          ^~~~~~
testSpeed.cpp:18:10: note: candidate: void Test::method() [with T = St1; Targs = {}]
     void method()
          ^~~~~~
testSpeed.cpp: In instantiation of ‘void Test::method() [with T = St1; Targs = {St2, St3}]’:
testSpeed.cpp:28:29:   required from here
testSpeed.cpp:20:18: error: call of overloaded ‘method()’ is ambiguous
         method<T>();
         ~~~~~~~~~^~
testSpeed.cpp:12:10: note: candidate: void Test::method() [with T = St1]
     void method()
          ^~~~~~
testSpeed.cpp:18:10: note: candidate: void Test::method() [with T = St1; Targs = {}]
     void method()
c++ variadic-templates
3个回答
1
投票

在C ++ 17中,这非常简单:

class Test {
public:
    template<typename T, typename... Ts>
    void method() {
        std::cout << typeid(T).name() << std::endl;
        if constexpr (sizeof...(Ts) > 0)
            method<Ts...>();
    }
};

在C ++ 11中,您可以这样做:

class Test {
    template<class> struct Tag {};

    template<typename T>
    void method_impl(Tag<T>) {
        std::cout << typeid(T).name() << std::endl;
    }

    template<typename T, typename... Ts>
    void method_impl(Tag<T> tag, Tag<Ts>... tags) {
        method_impl(tag);
        method_impl(tags...);
    }

public:
    template<typename... Ts>
    void method() {
        method_impl(Tag<Ts>{}...);
    }
};

稍加修改即可自动处理空包:

class Test {
    template<class> struct Tag {};

    void method_impl() { }

    template<typename T, typename... Ts>
    void method_impl(Tag<T>, Tag<Ts>... tags) {
        std::cout << typeid(T).name() << std::endl;
        method_impl(tags...);
    }

public:
    template<typename... Ts>
    void method() {
        method_impl(Tag<Ts>{}...);
    }
};

2
投票

当参数包为空时,您可以使用SFINAE禁用可变参数版本:

template<typename T, typename ...Targs, 
    std::enable_if_t<sizeof...(Targs) != 0>* = nullptr>
void method()
{
    method<T>();
    method<Targs...>();
}

Demo


0
投票

功能参数允许选择重载,而不是模板参数。

一种解决方案是将模板参数变成参数:

template <typename ... Ts> struct Tag{};

class Test {
public:
    template<typename T>
    void method(Tag<T>)
    {
        std::cout << typeid(T).name() << std::endl; // for example
    }

    template<typename T, typename ...Ts>
    void method(Tag<T, Ts...>)
    {
        method(Tag<T>{});
        method(Tag<Ts...>{});
    }
};

int main() {
    Test a;

    a.method(Tag<St1, St2, St3>{});
    a.method(Tag<St1>{});
}

Demo

template <typename T> struct Tag{};

class Test {
public:
    template<typename T>
    void method(Tag<T>)
    {
        std::cout << typeid(T).name() << std::endl; // for example
    }

    template<typename T, typename ...Ts>
    void method(Tag<T>, Tag<Ts>...)
    {
        method(Tag<T>{});
        method(Tag<Ts>{}...);
    }
};

int main() {
    Test a;

    a.method(Tag<St1>{}, Tag<St2>{}, Tag<St3>{});
    a.method(Tag<St1>{});
}

Demo

但是您可以摆脱递归调用(和重载):

C ++ 17中的折叠表达式:

template<typename ... Ts>
void method()
{
    ((std::cout << typeid(Ts).name() << std::endl), ...);
}

在C ++ 11 / C ++ 14中,它比较冗长,但不太清楚:

template<typename ... Ts>
void method()
{
    std::initializer_list<int>{0, ((std::cout << typeid(Ts).name() << std::endl), 0)...};
}
© www.soinside.com 2019 - 2024. All rights reserved.