【问题标题】:C++ Template Specialization with Constant Value具有常量值的 C++ 模板特化
【发布时间】:2011-06-17 23:16:25
【问题描述】:

在给定模板参数之一的数字常量的情况下,是否有一种直接的方法来定义 C++ 模板类的部分特化?我正在尝试仅为某些类型的模板组合创建特殊的构造函数:

template <typename A, size_t B> class Example
{
    public:
        Example() { };

        A value[B];
};

template <typename A, 2> class Example
{
    public:
        Example(b1, b2) { value[0] = b1; value[1] = b2; };
};

此示例无法编译,在第二个定义中返回错误 Expected identifier before numeric constant

我在这里和其他地方浏览了许多示例,但大多数似乎都围绕着专门处理类型而不是常量。

编辑:

寻找一种方法来编写一个有条件使用的构造函数,功能上是这样的:

template <typename A, size_t B> class Example
{
    public:
        // Default constructor
        Example() { };

        // Specialized constructor for two values
        Example<A,2>(A b1, A b2) { value[0] = b1; value[1] = b2; };

        A foo() {
          A r;

          for (size_t i = 0; i < b; ++b)
            r += value[i];

          return r;
        }

        // Hypothetical specialized implementation
        A foo<A, 2>() {
          return value[0] + value[1];
        }

        A value[B];
};

【问题讨论】:

  • 检查我的答案。它似乎完全符合您的要求。

标签: c++ templates template-specialization


【解决方案1】:

您需要将专业化放在正确的位置:

template <typename A> class Example<A,2>

如果要创建子类:

template <typename A> class ExampleSpecialization : public Example<A,2>

专门针对 typedef 的行为类似于专门针对整数参数的行为。

【讨论】:

  • 这似乎创建了一个完全不相关的类,其中未定义 value
  • 你可以写模板 class ExampleSpecialization : public Example -- answer updated
  • 我想我有点撞墙了。当调用泛型方法时,父类将引入自身的实例,但子类将发出不兼容的子类。我想在特定情况下没有办法编写条件构造函数和/或覆盖特定方法?
  • 你需要给你想做的事情多一点色彩
【解决方案2】:

我认为这可能有效:

#include <iostream>

template <typename A, size_t B>
class Example {
public:
    Example()
    {
        Construct<B>(identity<A, B>());
    }

    A foo()
    {
        return foo<B>(identity<A, B>());
    }

private:
    template <typename A, size_t B>
    struct identity {};

    template <size_t B>
    void Construct(identity<A, B> id)
    {
        for (size_t i = 0; i < B; ++i)
        {
            value[i] = 0;
        }
        std::cout << "default constructor\n";
    }

    template <size_t B>
    void Construct(identity<A, 2> id)
    {
        value[0] = 0;
        value[1] = 0;
        std::cout << "special constructor\n";
    }

    template <size_t B>
    A foo(identity<A, B> id)
    {
        A r = 0;
        for (size_t i = 0; i < B; ++i)
        {
            r += value[i];
        }
        std::cout << "default foo\n";
        return r;
    }

    template <size_t B>
    A foo(identity<A, 2> id)
    {
        std::cout << "special foo\n";
        return value[0] + value[1];
    }

    A value[B];
};

int main()
{
    Example<int, 2> example; // change the 2 to see the difference
    int n = example.foo();
    std::cin.get();
    return 0;
}

抱歉,我只是从我的测试项目中复制并粘贴了它。在某种程度上,它并不是真正的“专业化”,它只是将重载调用到专门的函数。我不确定这是否是您想要的,而且 imo 这不是很优雅。

【讨论】:

【解决方案3】:

如果没记错的话,应该更像:

template <typename A, size_t B> class Example
{
    public:
        Example() { };

        A value[B];
};

template <typename A> class Example<A, 2>
{
    public:
        Example(A b1, A b2) { value[0] = b1; value[1] = b2; };
};

我不认为这是完全允许的——在专用版本中没有定义b1 和/或b2 的类型。

编辑 [基于已编辑的问题]:是的,模板专业化会产生一种新类型,该类型与其专业化的基础并不真正相关。特别是,两者共享任何实现。根据非类型参数的值,您不能(通过专门化类模板)生成使用两个不同 ctor 之一的单一类型。

【讨论】:

  • 你需要指定两个模板参数
  • 是的,我对不同的选项进行了抨击,但它出现了错误,声明了一个不相关的类,或者一个不能完全正确地增加主类的子类。缺少参数类型是我糟糕的剪切和粘贴工作。
【解决方案4】:

你可以试试这样的:

template<size_t s>
struct SizeTToType { static const size_t value = s; };

template<bool> struct StaticAssertStruct;
template<> struct StaticAssertStruct<true> {};
#define STATIC_ASSERT(val, msg) { StaticAssertStruct<((val) != 0)> ERROR_##msg; (void)ERROR_##msg;}

template <typename A, size_t B> 
class Example
{
    public:
        Example() { };
        Example(A b1){ value[0] = b1; }
        Example(A b1, A b2) { 
                STATIC_ASSERT(B >= 2, B_must_me_ge_2); 
                value[0] = b1; value[1] = b2;
        } 
        A foo() { return in_foo(SizeTToType<B>()); }
    protected:
        template<size_t C>
        A in_foo(SizeTToType<C>) {
                cout << "univ" << endl;
                A r;
                for (size_t i = 0; i < B; ++i)
                r += value[i];
                return r;
        }
        A in_foo(SizeTToType<2>){
                cout << "spec" << endl;
                return value[0] + value[1];
        }
        A value[B];
};

http://www.ideone.com/wDcL7 上的工作示例

在模板中,如果您不使用方法,则编译代码中将不存在该方法,因此此解决方案不应使可执行文件变大,因为您不能与某些专用类一起使用的 ctors(例如 Example&lt;int, 1&gt; 不应该有Example(A b1, A b2)ctor)。

【讨论】:

  • 这是一种有趣的方法,但我正在寻找的技巧是在构造函数定义中声明完整的类和模板参数。
  • 与将STATIC_ASSERT 添加到此类构造函数有何不同?不能在代码中使用,所以代码不存在。
【解决方案5】:

如果您的目标是只需要在您的专业中重写一些方法/构造函数,那么可以考虑使用通用基类来保存所有 Example 模板的通用实现,这样您就不必重写它你想出的每一个专业。

例如:

template < typename A, size_t B >
class ExampleGeneric {
public:

  // generic implementation of foo inherited by all Example<A,B> classes
  void foo() {
    A r;

    for (size_t i = 0; i < B; ++i)
      r += value[i];

    return r;
    }

  // generic implementation of bar inherited by all Example<A,B> classes
  void bar() {
    A r;

    for (size_t i = 0; i < B; ++i)
      r *= value[i];

    return r;
    }

  A values[B];
  };

template < typename A, size_t B >
class Example : public ExampleGeneric<A,B> {
public:
  //default to generic implementation in the general case by not overriding anything
  };

//*** specialization for 2
template < typename A >
class Example<A,2> : public ExampleGeneric<A,2>{
public:

  // has to be provided if you still want default construction
  Example() {
    }

  //extra constructor for 2 parameters
  Example( A a1, A a2 ) {
    values[0] = a1;
    values[1] = a2;
    }

  // specialization of foo
  void foo() {
    return values[0] + values[1];
    }

  // don't override bar to keep generic version
  };

【讨论】:

    【解决方案6】:
    #include <iostream>
    
    using namespace std;
    
    
    template<typename _T, size_t S>
    class myclass {
        _T elem[S];
    public:
        myclass() {
            for (int i = 0; i < S; i++) {
                elem[i] = i;
            }
        }
        void Print() {
            for (int i = 0; i < S; i++) {
                cout << "elem[" << i << "] = " << elem[i] << endl;
            }
        }
    };
    
    
    int main(int argc, char **argv)
    {
        myclass < int, 10 > nums;
        nums.Print();
        myclass < int, 22 > nums1;
        nums1.Print();
    }
    

    这在我的 linux 机器上工作

    g++ (GCC) 4.1.2 20080704 (红帽 4.1.2-48) 版权所有 (C) 2006 Free Software Foundation, Inc. 这是免费软件;查看复制条件的来源。没有 保修单;甚至不考虑适销性或特定用途的适用性。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-04-06
      • 2023-03-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多