C++ 模板类,其方法取决于模板参数

C++ 模板类,其方法取决于模板参数,c++,c++11,templates,C++,C++11,Templates,我正在编写一个旨在拍摄随机3D向量的类,但我在我的项目中使用了几个几何库(一个包含在3D模拟中,一个包含在分析框架中,一个不包含在超过1GB的框架中…)。每个库都有自己的向量定义,同一方法有不同的名称,例如getX()、getX()、Get(0)。。。得到第一个笛卡尔坐标。但有时会采用通用的命名约定,并且某些方法名称在两个或多个库中是相同的。 当然,我想对这些向量中的任何一个使用这段代码,所以我实现了一个模板类。问题是:如何使代码适应所有这些方法名称,而不为每个实现专门化类(有些实现共享相同的方

我正在编写一个旨在拍摄随机3D向量的类,但我在我的项目中使用了几个几何库(一个包含在3D模拟中,一个包含在分析框架中,一个不包含在超过1GB的框架中…)。每个库都有自己的向量定义,同一方法有不同的名称,例如getX()、getX()、Get(0)。。。得到第一个笛卡尔坐标。但有时会采用通用的命名约定,并且某些方法名称在两个或多个库中是相同的。
当然,我想对这些向量中的任何一个使用这段代码,所以我实现了一个模板类。问题是:如何使代码适应所有这些方法名称,而不为每个实现专门化类(有些实现共享相同的方法名称)?
我设法用一种或另一种方法编写了一个类,现在我想推广到任意数量的方法。上面写着:“如果你有方法1,使用这个实现,如果你有方法2,使用另一个,…如果你没有,那么编译错误”

当前该类看起来像(简化为随机方向拍摄的部分):

//首先使用一些模板来测试某些方法的存在性
名称空间详细信息{
//测试一个类是否包含“setRThetaPhi”方法
模板
静态自动测试\u setRThetaPhi(int)->
decltype(void(std::declval().setRThetaPhi(0,0,0.)),
std::真_类型{});
模板
静态自动测试设置基准(浮动)->std::false类型;
}
//如果类包含“setRThetaPhi”方法,则为true_类型
模板
结构有_setRThetaPhi:decltype(detail_rand::test_setRThetaPhi(0)){};
//实际班级
模板
类随机
{
//一切都是静态的,便于使用,以后可能会更改
私人:
随机()=删除;
随机(随机&)=删除;
//分布、随机发生器及其种子
静态decltype(std::chrono::high_resolution_clock::now();
静态标准::默认随机引擎生成器;
静态标准::均匀实分布均匀实分布;
//拍摄一个方向,实际执行是在文件的末尾
private://不同的实现
静态常量向量Dir_impl(std::true_type const&);
静态常量向量Dir_impl(std::false_type const&);
public://实现的包装器
内联静态常量向量方向(){
return Dir_impl(has_setRThetaPhi());
}
};
///成员初始化(静态,但在页眉中为模板)
//种子不具有加密质量,但在这里它不相关
模板
decltype(std::chrono::高分辨率时钟::now().time\u自\u epoch().count())
随机::主题=
std::chrono::高分辨率时钟::now().time\u自\u epoch().count();
模板
std::default\u random\u engine random::生成器(theSeed);
模板
标准::均匀实分布随机::均匀实分布(0,1.);
///根据向量的实际类型实现方法
//这里我使用“setRThetaPhi”方法
模板
常量向量Random::Dir\u impl(std::true\u type常量&)
{
向量v;
v、 setRThetaPhi(1.)。,
标准::acos(1.-2.*统一真实发行版(theGenerator)),
TwoPi()*均匀实分布(生成器);
返回std::move(v);
}
//这里我使用“SetMagThetaPhi”方法作为默认值
//但是我想先测试一下我是否真的有这个方法,
//并定义以编译错误结尾的默认实现
//(通过静态_断言)
模板
常量向量Random::Dir\u impl(std::false\u type常量&)
{
向量v;
v、 SetMagThetaPhi(1。,
标准::acos(1.-2.*统一真实发行版(theGenerator)),
TwoPi()*均匀实分布(生成器);
返回std::move(v);
}

您可以通过为操作引入自己的名称来解决此问题。为此,创建一个trait类并为每个库专门化它。大概是这样的:

template <class Vector>
struct VectorTraits;

template <>
struct VectorTraits<Lib1::Vector>
{
  static auto getX(const Lib1::Vector &v) { return v.GetX(); }
  // ... etc.
};

template <>
struct VectorTraits<Lib2::Vector>
{
  static auto getX(const Lib2::Vector &v) { return v.Get(0); }
  // ... etc.
};

//Usage:

template <class vector>
auto norm2(const vector &v)
{
  using V = VectorTraits<vector>;
  return V::getX(v) * V::getX(v) + V::getY(v) + V::getY(v);
}
模板
结构向量;
模板
结构向量
{
静态自动getX(constlib1::Vector&v){return v.getX();}
//……等等。
};
模板
结构向量
{
静态自动getX(constlib2::Vector&v){return v.Get(0);}
//……等等。
};
//用法:
模板
自动规范2(常量向量和v)
{
使用V=矢量;
返回V::getX(V)*V::getX(V)+V::getY(V)+V::getY(V);
}
如果要对不受支持的操作使用静态断言,可以将它们放入非专用模板中:

template <class T>
struct False : std::false_type {};

template <class Vector>
struct VectorTraits
{
  static void getX(const Vector &)
  {
    static_assert(False<Vector>::value, "This type does not support getting x");
  }
};
模板
struct False:std::False_type{};
模板
结构向量
{
静态void getX(常量向量&)
{
静态_断言(False::value,“此类型不支持获取x”);
}
};
上面写着:“如果你有方法1,使用这个实现,如果你有方法2,使用另一个,…如果你没有,那么编译错误”

我写了一篇文章,解释了如何在C++11、C++14和C++17中实现您所需要的东西:

我将在下面综合C++11和C++14解决方案-您可以使用它们来规范化您正在处理的所有接口,方法是将它们封装在一个“通用”接口中。然后,您可以在“公共”接口上实现您的算法


假设您有:

struct Cat { void meow() const; };
struct Dog { void bark() const; };
您需要创建一个函数模板
make_noise(const T&x)
,如果有效,则调用
x.meow()
,否则
x.bark()
如果有效,则生成编译器错误


在C++11中,可以使用
enable_if

您需要为希望检查其存在性的每个成员创建一个类型trait。例如:

template <typename, typename = void>
struct has_meow : std::false_type { };

template <typename T>
struct has_meow<T, void_t<decltype(std::declval<T>().meow())>>
    : std::true_type { };

在C++14中,您可以使用通用lambda和
static\u if
(这里是一个关于可能的lambda)的实现来使用命令式语法执行检查

您需要一些实用程序:

// Type trait that checks if a particular function object can be 
// called with a particular set of arguments.
template <typename, typename = void>
struct is_callable : std::false_type { };

template <typename TF, class... Ts>
struct is_callable<TF(Ts...),
    void_t<decltype(std::declval<TF>()(std::declval<Ts>()...))>>
    : std::true_type { };

// Wrapper around `is_callable`.
template <typename TF>
struct validity_checker
{
    template <typename... Ts>
    constexpr auto operator()(Ts&&...) const
    {
        return is_callable<TF(Ts...)>{};
    }
};

// Creates `validity_checker` by deducing `TF`.
template <typename TF>
constexpr auto is_valid(TF)
{
    return validity_checker<TF>{};
}

一些宏黑魔法和
if constexpr
允许您编写
template <typename T>
auto make_noise(const T& x)
    -> typename std::enable_if<has_meow<T>{}>::type
{
    x.meow();
}

template <typename T>
auto make_noise(const T& x)
    -> typename std::enable_if<has_bark<T>{}>::type
{
    x.bark();
}
// Type trait that checks if a particular function object can be 
// called with a particular set of arguments.
template <typename, typename = void>
struct is_callable : std::false_type { };

template <typename TF, class... Ts>
struct is_callable<TF(Ts...),
    void_t<decltype(std::declval<TF>()(std::declval<Ts>()...))>>
    : std::true_type { };

// Wrapper around `is_callable`.
template <typename TF>
struct validity_checker
{
    template <typename... Ts>
    constexpr auto operator()(Ts&&...) const
    {
        return is_callable<TF(Ts...)>{};
    }
};

// Creates `validity_checker` by deducing `TF`.
template <typename TF>
constexpr auto is_valid(TF)
{
    return validity_checker<TF>{};
}
template <typename T>
auto make_noise(const T& x)
{
    auto has_meow = is_valid([](auto&& x) -> decltype(x.meow()){ });
    auto has_bark = is_valid([](auto&& x) -> decltype(x.bark()){ });

    static_if(has_meow(x))
        .then([&x](auto)
            {
                x.meow();
            })
        .else_if(has_bark(x))
        .then([&x](auto)
            {
                x.bark();
            })
        .else_([](auto)
            {
                // Produce a compiler-error.
                struct cannot_meow_or_bark;
                cannot_meow_or_bark{};
            })(dummy{});
}
template <typename T>
auto make_noise(const T& x)
{
    if constexpr(IS_VALID(T)(_0.meow()))
    {
        x.meow();
    }
    else if constexpr(IS_VALID(T)(_0.bark()))
    {
        x.bark();
    }
    else
    {
        struct cannot_meow_or_bark;
        cannot_meow_or_bark{};
    }
}