带有条件类型名称的模板类

20

我希望能够有一个模板类(例如float/double类型),但是我正在使用Nvidia CUDAOptiX,并且有多种其他类型(例如float2double2float3等),这些类型都取决于所选择的模板类型。

类似于下面这样:

#include <optixu/optixu_vector_types.h>
#include <type_traits>

template <class T>
class MyClass 
{
   MyClass()
   {
      if (std::is_same<T, float>::value) 
      {
         typedef optix::float2 T2;
      }
      else if (std::is_same<T, double>::value)
      {
         typedef optix::double2 T2;
      }

      T2 my_T2_variable;
   }

   void SomeFunction() 
   { 
      T2 another_T2_variable; 
   };
};

目前我的解决方案是使用多个模板参数 MyClass<T,T2,T3> my_object;,但这样会有太多的开销和杂乱无章。是否有一种方法可以只使用单个模板参数来实现所需的相同效果?


1
C++20的概念是什么?https://en.cppreference.com/w/cpp/language/constraints - foragerDev
4
如果有解决方案的话,我希望能够坚持使用C++11。 - SemtexB
3个回答

21

通常情况下,您可以通过创建一个特质类型,其专业化定义了其他类型来实现此目的。例如:

// Base template is undefined.
template <typename T>
struct optix_traits;

template <>
struct optix_traits<float> {
    using dim2 = optix::float2;
    // etc
};

template <>
struct optix_traits<double> {
    using dim2 = optix::double2;
    // etc
};

如果需要,您可以将这些类型的别名指定为您类型中的名称:

template <typename T>
class MyClass {
public:
    using T2 = typename optix_traits<T>::dim2;
};

可以了,谢谢。顺便提一下:我刚刚注意到我有一些返回T2/T3的私有成员函数,但它会抛出一个关于未知(返回)类型T2的错误?例如:T2 MyClass<T>::Foo(){return my_T2_variable;} 有什么解决办法吗?还是我应该发一个新问题? - SemtexB
2
@SemtexB 一个类外成员函数的定义在它知道该成员属于哪种类型之前是不在作用域内的。当它看到 T2 时,它并不知道 T2 是什么,因为它还不知道这个方法在 MyClass<T> 中。详细说明一下:template <typename T> typename MyClass<T>::T2 MyClass<T>::Foo() { ... } 自 C++11 起,您还可以使用 auto 来移动返回类型,就像这样:template <typename T> auto MyClass<T>::Foo() -> T2 { ... }。在这种情况下,返回类型出现在 MyClass<T>:: 之后,它知道要在 MyClass<T> 中查找它。 - cdhowie
太好了,成功了。感谢您的快速回复。我尝试过 template <typename T> MyClass::T2 MyClass<T>::Foo() { ... },但是忘记了 typename 关键字和模板参数...哎呀! - SemtexB
1
总结:虽然所有的模板都是元函数,但类型特征是我们在想要通过列举所有可能性来定义它们时使用的特定习语,其中函数的输出是关于类型的一堆“事实”(或者您可以称之为类型的属性或特征)。因此,从一个类型列表到另一个类型列表的简单映射几乎就是一种类型特征,无论您是否考虑将其命名为这样。 - Steve Jessop

15
你可以使用<type_traits>中的std::conditional
如果要在T == float时使T2optix::float2,否则为optix::double2,请使用std::conditional。自从,这个函数可以在编译期解析类型T2
#include <type_traits>  // std::conditional, std::is_same

template <class T>
class MyClass
{
    using T2 = typename std::conditional<std::is_same<T, float>::value,
                                          optix::float2, optix::double2>::type;
    T2 my_T2_variable;

    // ... other code
};

(查看演示)


正如@HikmatFarhat所指出的那样,std::conditional不能捕捉到用户错误。它只检查第一个条件,并对false情况给出类型optix::double2
另一个选择是一系列SFINAE函数,并使用decltype将其用于T2,如下所示:
#include <type_traits>  // std::is_same, std::enable_if

template <class T> // uses if T == float and return `optix::float2`
auto typeReturn() -> typename std::enable_if<std::is_same<float, T>::value, optix::float2>::type { return {}; }

template <class T> // uses if T == double and return `optix::double2`
auto typeReturn() -> typename std::enable_if<std::is_same<double, T>::value, optix::double2>::type { return {}; }

template <class T>
class MyClass
{
    using T2 = decltype(typeReturn<T>()); // chooses the right function!

    T2 my_T2_variable;

    // ... other codes
};

(查看演示)


1
是的,但如果他犯了一个错误,std::conditional将无法捕获它。例如,使用std::string调用它时,默认为optix::double2。而type_trait解决方案会给出一个错误。 - Hikmat Farhat
3
@HikmatFarhat 你是正确的。我添加了另一个选项,它类似于 type_trait 解决方案,并应该涵盖此问题。 - JeJo

6
使用模板特化实现元函数,将标准C++类型映射到具有所需“rank”级别的OptiX类型。
template <typename T, std::size_t N> struct optix_type;

template <> struct optix_type<float, 2> { using type = optix::float2; };
template <> struct optix_type<float, 3> { using type = optix::float3; };
template <> struct optix_type<double, 2> { using type = optix::double2; };
// ...

template <typename T, std::size_t N>
using optix_type_t = typename optix_type<T, N>::type;

您可以在类中使用这个方法,轻松地获取正确的类型:

template <class T>
class MyClass {
  using T2 = optix_type_t<T, 2>;
  MyClass() {
    T2 my_T2_variable;
    optix_type_t<T, 3> my_T3_variable;
  }
  void SomeFunction() { T2 another_T2_variable; };
};

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接