21

如何在 C++ 模板中有多个类型名参数?

#ifndef _CALL_TEMP_H
#define _CALL_TEMP_H

#include <string>
#include <iostream>

template <typename Sig>
class Foo;

template <typename A, typename B>
class Foo
{
    public:
        void output() {
            std::cout << a_ << b_ << std::endl;
        }
        A a_;
        B b_;
};

template <typename A, typename B, typename C>
class Foo
{
    public:
        void output() {
            std::cout << a_ << b_ << c_ << std::endl;
        }
        A a_;
        B b_;
        C c_;
};

#endif

用法:

int main()
{
    Foo<int ,int> doubleint;
    doubleint.a_ = 1;
    doubleint.b_ = 2;
    doubleint.output();
//  Foo<int , int , std::string> comp;
//  comp.a_ = 1;
//  comp.b_ = 2;
//  comp.c_ = "haha";
//  comp.output();
    return 0;
}

但它不会编译。我怎样才能让它编译?

4

4 回答 4

45

只需使用可变参数模板声明一个主模板,然后专门针对每个支持的模板参数数量。例如:

#ifndef CALL_TEMP_H
#define CALL_TEMP_H

#include <iostream>

template <typename...> class Foo;

template <typename A, typename B>
class Foo<A, B>
{
public:
    void output() {
        std::cout << a_ << b_ << '\n';
    }
    A a_;
    B b_;
};

template <typename A, typename B, typename C>
class Foo<A, B, C>
{
public:
    void output() {
        std::cout << a_ << b_ << c_ << '\n';
    }
    A a_;
    B b_;
    C c_;
};

#endif

我不能使用 C++11,并且想要保留类似的符号,您需要使用模板默认参数来模拟可变参数列表。这将隐式限制模板参数的数量,但由于无论如何您都在专门化模板,所以这个限制并不重要。

如果可以接受使用不同的符号,您还可以使用看起来像函数声明的东西来实例化和专门化您的模板:

template <typename> class Foo;

template <typename A, typename B>
class Foo<void(A, B)> {
    ...
};
template <typename A, typename B, typename C>
class Foo<void(A, B, C)> {
    ...
};
...
Foo<void(int, int)>                   f2;
Foo<void(int, int, std::string)> f3;

符号的更改是否可以接受取决于您对类模板的使用。但是,如果没有 C++11,您将无法获得理想的解决方案。

顺便说一句,不要过度使用std::endl:用于'\n'表示行尾。如果您真的要刷新流,请使用std::flush. 也是_CALL_TEMP_H标准 C++ 库保留的名称,所有以下划线后跟大写字符的名称也是如此:除非有使用它们的明确许可,否则不要在您自己的代码中使用这些名称(例如__FILE____LINE__保留但明确许可允许使用它们)。

于 2013-11-12T07:44:53.240 回答
3

如果您有多个版本的模板,则必须专门化一个版本。如果您想要不同数量的参数,那么诀窍是使用标记类说“此参数没有参数”,并将其作为默认参数。

在您的情况下,类似以下工作(编译和测试):

#include <iostream>

// tag class indicating "no member in this place"
struct nothing {};

template <typename A, typename B, typename C = nothing> // <- note default arg.
class Foo;

template <typename A, typename B>
class Foo<A, B, nothing> // <- note specialization
{
    public :
        void output() {
            std::cout << a_ << b_ << std::endl;
        }

        A a_;
        B b_;
};

template <typename A, typename B, typename C>
class Foo
{
    public :
        void output() {
            std::cout << a_ << b_ << c_ << std::endl;
        }

        A a_;
        B b_;
        C c_;
};

int main()
{
    Foo<int, int> doubleint;
    doubleint.a_ = 1;
    doubleint.b_ = 2;
    doubleint.output();

    Foo<int, int, int> tripleint;
    tripleint.a_ = 1;
    tripleint.b_ = 2;
    tripleint.c_ = 3;
    tripleint.output();
}

请注意,这本质上是对 boost::tuple<>/std::tuple<> 的重新发明,您绝对应该阅读它。

于 2013-11-12T07:40:35.710 回答
0

我认为您将专业化与重载相同的类名混淆了。您不能使用多个模板参数创建具有相同名称的类。

于 2013-11-12T07:40:03.040 回答
0

它不会编译,因为您不能使用不同数量的模板参数多次定义同一个类。

如果您知道要支持的模板参数的最大数量,则可以使用部分特化:

// main template
template <typename A, typename B = void, typename C = void>
struct Foo
{
    void output() { std::cout << a_ << b_ << c_ << std::endl; }
    A a_;
    B b_;
    C c_;
};

// Partial specialisation for two parameters
template <typename A, typename B>
struct Foo<A, B, void>
{
    void output() { std::cout << a_ << b_ << c_ << std::endl; }
    A a_;
    B B_;
};

// Partial specialisation for one parameter
template <typename A>
struct Foo<A, void, void>
{
    void output() { std::cout << a_ << std::endl; }
    A a_;
};

如果您使用的是 C++11,另一种选择是使用可变参数模板。

于 2013-11-12T07:49:04.480 回答