如何保证C ++模板类专业之间的接口一致?

Ian*_*ann 5 c++ template-specialization

问:C ++中有什么编译时机制可用于自动验证模板类方法的集合是否从类专业化到专业化相匹配?

示例:让我们假设我想要一个类接口,该类接口的行为取决于模板值的特殊性:

// forward declaration of template class name
template <typename T, bool isZero> class MyClass;

// partial class specialization for isZero = false case
template <typename T> class MyClass <T, false>
{
   T _val;

public:
   MyClass(T value) { _val = value; }
   T GetValue(void) const { return _val; }
};

// partial class specialization for isZero = true case
template <typename T> class MyClass <T, true>
{
public:
   MyClass(T value) {}
   T GetValue(void) const {return T(0);}
};
Run Code Online (Sandbox Code Playgroud)

这里的想法是,在星期二进行编译时,MyClass.GetValue()返回0,而在一周中的任何其他日子,我们都会得到期望值...或类似的东西。细节和动机并不重要。;-)

现在,虽然这似乎是基于部分类专业化来实现部分函数专业化的一种方法,但由于两个类专业化(AFAIK)可能具有完全不一致的接口,因此似乎也很混乱。如果我实际上要在生产环境中使用此机制,则如果我错误地将某些方法添加到某些专业化而不是其他方法而不是将某些方法添加到某些专业化,或者忘记某个地方的const等,我希望获得一些理想的编译时间通知。我该如何获得呢?

为了获得加分,让我们假设我不希望意外地编译isZero以外的其他值(真/假),如果我提供了MyClass的通用实现,可能会发生这种情况,并且让我们也怀疑我会增加运行时成本从这里将virtual添加为纯虚拟基类方法。

这似乎是一种非常明显的语言功能,以至于我可能在这里缺少树木的森林,也许我已经通过某种机制获得了这种行为,但尚未意识到。

> cat test.cpp                   
#include <stdio.h>

// forward declaration of template class name
template <typename T, bool isZero> class MyClass;

// partial specialization for isZero = false case
template <typename T> class MyClass <T, false>
{
   T _val;

public:
   MyClass(T value) { _val = value; }
   T GetValue(void) const { return _val; }
};

// partial specialization for isZero = true case
template <typename T> class MyClass <T, true>
{
public:
   MyClass(T value) {}
   T GetValue(void) const {return T(0);}
};

int main( void )
{
   MyClass<float, false>  one(1);
   MyClass<float, true> notOne(1);

   printf( "%f, %f\n", one.GetValue(), notOne.GetValue());
   return 0;
}
> clang -Wall -pedantic test.cpp 
> ./a.out                        
1.000000, 0.000000
Run Code Online (Sandbox Code Playgroud)

Nic*_*asM 3

您可以在使用时进行静态断言:

template <typename T>
class MyOtherClass
{
    static_assert(std::is_same_v<decltype(MyClass<T, true >{T{}}.GetValue()), T>);
    static_assert(std::is_same_v<decltype(MyClass<T, false>{T{}}.GetValue()), T>);

    ... 

};
Run Code Online (Sandbox Code Playgroud)

如果您发现自己在多个地方做出了此断言,您还可以尝试定义一个定义/标识接口的特征类。

因为您要保留未专门化的模板参数(即T),所以这些特征有点尴尬,但这些可能会起作用:

// This traits class will inherit from either 
// std::true_type or std::false_type.

template <template <typename, bool> S, typename T>
struct is_value_provider : std::intergral_constant<bool, 
    std::is_same_v<decltype(S<T, true >{T{}}.getValue()), T> &&
    std::is_same_v<decltype(S<T, false>{T{}}.getValue()), T>>
{}

template <template <typename, bool> S, typename T>
using is_value_provider_v = is_value_provider::value;


// Usage examples:
static_assert(is_value_provider_v<MyClass, int>);
static_assert(is_value_provider_v<MyClass, float>);
static_assert(is_value_provider_v<MyClass, double>);
Run Code Online (Sandbox Code Playgroud)