C++ 模板中的多个类型名参数?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19923353/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 23:15:57  来源:igfitidea点击:

Multiple typename arguments in c++ template?

c++templatestemplate-specialization

提问by Jichao

How can I have multiple typename arguments in a c++ template?

如何在 C++ 模板中有多个 typename 参数?

#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

Usage:

用法:

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;
}

But it will not compile. How could I make it compile?

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

回答by Dietmar Kühl

Just declare a primary template with a variadic template and then specialize for each supported number of template arguments. For example:

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

#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

I you can't use C++11 and you want to retain a similar notation you'll need to simulate a variadic argument list with template default arguments. This will implicitly limit the number of templare arguments but since you are specializing the templates anyway, this limitation doesn't realky matter.

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

If it is acceptable to use a different notation you can also use something which looks like a function declaration to instantiate and specialize your template:

如果可以使用不同的符号,您还可以使用类似于函数声明的东西来实例化和专门化您的模板:

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;

Whether the change in notation is acceptable depends on your use of the class template. You won't achieve an ideal solution as with variadic templates without C++11, though.

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

BTW, don't overuse std::endl: use '\n'to mean end of line. If you really mean to flush the stream, use std::flush. Also _CALL_TEMP_His a name reserved to the standard C++ library as are all names starting with an underscore followed by a capital character: do notuse these names in your own code unless there is explicit permission to use them (e.g. __FILE__and __LINE__are reserved but explicit permission to use them is granted).

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

回答by Kaz Dragon

If you have multiple versions of a template, you have to specialize a single version. If you want a different number of arguments, then the trick is to use a tag class to say "this argument is no argument", and have that as a default argument.

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

In your case, something like the following works (compiled and tested):

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

#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();
}

Note that this is essentially a re-invention of boost::tuple<>/std::tuple<>, which you should definitely read up on.

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

回答by legends2k

I think you're confusing specialization with overloading the same class name. You cannot create a class with the same name with multiple template arguments.

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

回答by Tristan Brindle

It won't compile because you can't have the same class defined multiple times with a different number of template arguments.

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

If you know the maximum number of template parameters you want to support, you could use partial specialisation:

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

// 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_;
};

If you're using C++11, another option would be to use variadic templates.

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