【问题标题】:Multiple typename arguments in c++ template?c++模板中的多个类型名参数?
【发布时间】:2013-11-24 06:11:48
【问题描述】:

如何在 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;
}

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

【问题讨论】:

    标签: c++ templates template-specialization


    【解决方案1】:

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

    #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,并且您想保留类似的符号,您需要使用模板默认参数模拟可变参数列表。这将隐式限制 templare 参数的数量,但由于无论如何您都在专门化模板,所以这个限制并不重要。

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

    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,您将无法获得理想的解决方案。

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

    【讨论】:

    • +1 但在 Windows 中只有 Visual Studio 2013 支持可变参数模板参数,所以我更喜欢 c++98 之一。
    • @Jichao:在windows中你有完美的MinGW/GCC。
    • +1 并接受作为 c++98 解决方案的答案。我在chrome的回调系统中看到了这种技术...^^
    • @MM.:但是标准的生产环境是visual c++,如果可能的话我需要写兼容的代码。
    • 现在使用这种可变参数语法,我们怎么能专门化一些“类型名”呢?我在 C++ 2003 上引用。
    【解决方案2】:

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

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

    #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 的重新发明,您一定要仔细阅读。

    【讨论】:

    • 另请注意,这是 C++98 的答案,而 Dietmar 的(非常好的)答案是 C++11 的答案。
    【解决方案3】:

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

    【讨论】:

      【解决方案4】:

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

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

      // 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,另一种选择是使用可变参数模板。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-01-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-07-09
        相关资源
        最近更新 更多